LeetCode刷题总结-树篇(上)
引子:刷题的过程可能是枯燥的,但程序员们的日常确不乏趣味。分享一则LeetCode上名为《打家劫舍 |||》题目的评论:
如有兴趣可以从此题为起点,去LeetCode开启刷题之旅,哈哈。该题目是选择一颗二叉树中对应节点的问题,也是本文收录的一道例题(具体请参考例12)。
本文开始分享作者对于LeetCode上有关树的刷题总结。谈到树,很多初学者会感觉很头疼。头疼的重点是其很多解法都离不开递归(或者说是深度优先搜索)的应用。而递归的难点在于其有很多返回值,对于这些返回值的顺序很难理顺,即代码虽短,但理解很烧脑。因此,对递归思想理解不够深的同学,建议先看作者的另一篇文章《LeetCode刷题总结-递归篇》,然后再开启攻克有关树的相关习题之旅(PS:这样会起到事半功倍的效果噢)。
在LeetCode的标签分类题库中,和树有关的标签有:树(123道题)、字典树(17道题)、线段树(11道题)、树状数组(6道题)。对于这些题,作者在粗略刷过一遍后,对其中的考点进行了总结,并归纳为以下四大类:
- 树的自身特性
- 树的类型
- 子树问题
- 新概念定义问题
对于上述四类考点,作者通过分析对比同类型考点的题目,选取其中比较经典或者有代表性的题目作为例题(共计收录约45道题)。在减少题量的同时,也希望能够全面覆盖LeetCode上关于树的相关习题的考点。作者计划分为三篇文章来讲解,本文是该系列的上篇,讲解考察树的自身特性相关考点的习题。选取的例题共21道,其中简单题5道、中等题13道、困难题3道。
关于树的自身特性总结归纳为四个问题:基本特性问题、构造问题、节点问题和路径问题,具体如下图所示。
树基本特性问题:请参考下文例1至例8。
树的构造问题:请参考下文例9、例10。
树的节点问题:请参考下文例11至例16。
树的路径问题:请参考下文例17至例21。
对于上述四个问题,基本特性和构造问题只需刷过一遍即可理解相关解法。对于树的节点和路径问题,则是本文例题中的相对困难的习题,一般需要重复刷或者深度分析和琢磨,才能感悟普适解法的套路。其中,在有关树的路径问题中,本文未收录树的前、中、后和层次遍历问题的习题,这些题目默认为较为基础的习题。
例1 对称二叉树
题号:101,难度:简单
题目描述:
解题思路:
递归思想的一个简单应用,从以树的根节点的左右子节点为根开始进行深度优先搜索,依次判断两颗子树的左子树是否更与其右子树,右子树是否等于其左子树即可。如果采用迭代则只需使用层次遍历,判断每层元素是否满足镜像对称即可。
具体代码:
class Solution {
public boolean isSymmetric(TreeNode root) {
if(root == null)
return true;
return dfs(root.left, root.right);
} public boolean dfs(TreeNode left, TreeNode right) {
if(left == null && right == null)
return true;
if(left == null || right == null || left.val != right.val)
return false;
return dfs(left.left, right.right) && dfs(left.right, right.left);
}
}
运行结果:
例2 翻转二叉树以匹配前序遍历
题号:971,难度:中等(关于翻转类习题,还可以参考题号226和951)
题目描述:
解题思路:
该题也是递归思想的应用。按照题目要求进行前序遍历,一旦遇到对应值与目标数组结果不同时,翻转遍历,接着继续遍历,如果最终结果依然不匹配则返回false,否则返回true。
具体代码:
class Solution {
private int index;
private int[] voyage;
private List<Integer> result; public List<Integer> flipMatchVoyage(TreeNode root, int[] voyage) {
// index = 0;
this.voyage = voyage;
result = new ArrayList<>();
dfs(root);
// System.out.println("result = "+result);
if(result.size() > 0 && result.get(result.size()-1) == -1)
return new ArrayList<Integer>(Arrays.asList(-1));
return result;
} public void dfs(TreeNode root) {
if(root == null)
return;
if(root.val != voyage[index++])
result.add(-1);
else {
if(root.left != null && root.left.val != voyage[index]) {
result.add(root.val);
dfs(root.right);
dfs(root.left);
} else {
dfs(root.left);
dfs(root.right);
}
}
}
}
运行结果:
例3 输出二叉树
题号:655,难度:中等
题目描述:
解题思路:
此题是要求以二维数组的形式画出给定的二叉树。需要建立一个以根节点为原点的平面直角坐标系,然后依据广度优先搜索(即层次遍历)的思想依次初始化每层数组中元素的值即可,其中应用到了二分查找来确定每个元素的具体坐标,能够有效降低检索时间。
具体代码:
class Solution {
public List<List<String>> printTree(TreeNode root) {
List<List<String>> result = new ArrayList<>();
int dep = getDepth(root);
Queue<TreeNode> queue = new LinkedList<>();
queue.add(root);
// System.out.println("dep = "+dep);
for(int i = 0;i < dep;i++) {
List<String> list = new ArrayList<>();
for(int j = 0;j < Math.pow(2, dep)-1;j++)
list.add("");
List<Integer> index = new ArrayList<>();
getIndex(i, 0, list.size() - 1, index);
for(int j = 0;j < Math.pow(2, i);j++) {
TreeNode temp = queue.poll();
if(temp == null) {
queue.add(temp);
queue.add(temp);
} else {
list.set(index.get(j), ""+temp.val);
queue.add(temp.left);
queue.add(temp.right);
}
}
result.add(list);
}
return result;
} public int getDepth(TreeNode root) {
if(root == null)
return 0;
return 1 + Math.max(getDepth(root.left), getDepth(root.right));
} public void getIndex(int num, int left, int right, List<Integer> index) {
int mid = (left + right) / 2;
if(num == 0)
index.add(mid);
else {
getIndex(num - 1, left, mid - 1, index);
getIndex(num - 1, mid + 1, right, index);
}
}
}
运行结果:
例4 合并二叉树
题号:617,难度:简单
题目描述:
解题思路:
此题比较简单,选取其中一个根节点作为返回值的根节点。然后应用深度优先搜索的思想,采用相同顺序同时遍历两棵树,如果当前节点均存在则相加,否则则选取含有值的节点。
具体代码:
class Solution {
public TreeNode mergeTrees(TreeNode t1, TreeNode t2) {
if(t1 == null)
return t2;
else if(t2 == null)
return t1;
t1.left = mergeTrees(t1.left, t2.left);
t1.right = mergeTrees(t1.right, t2.right);
t1.val = t1.val + t2.val; return t1;
}
}
运行结果:
例5 二叉树剪枝
题号:814,难度:中等(另外,还可以参考题号669,修剪二叉搜索树)
题目描述:
解题思路:
此题属于二叉树节点删除问题的实际应用,并且结合深度优先搜索(前序遍历的应用)和回溯的思想。具体实现过程请参考下方代码。
具体代码:
class Solution {
public TreeNode pruneTree(TreeNode root) {
if(root == null)
return root;
if(root.val == 0 && root.left == null && root.right == null)
root = root.left;
else {
root.left = pruneTree(root.left);
root.right = pruneTree(root.right);
} if(root != null && root.val == 0 && root.left == null && root.right == null)
root = root.left; return root;
}
}
运行结果:
例6 二叉树的右视图
题号:199,难度:中等
题目描述:
解题思路:
层次遍历的实际应用。只需依次保存每层最右边的一个节点即可。
具体代码:
class Solution {
public List<Integer> rightSideView(TreeNode root) {
if(root == null)
return new ArrayList<Integer>();
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
List<Integer> result = new ArrayList<>();
while(queue.size() > 0) {
int count = queue.size();
while(count-- > 0) {
TreeNode temp = queue.poll();
if(count == 0)
result.add(temp.val);
if(temp.left != null)
queue.offer(temp.left);
if(temp.right != null)
queue.offer(temp.right);
}
} return result;
}
}
运行结果:
例7 二叉树的最小深度
题号:111,难度:简单(最大深度请参考题号:104)
题目描述:
解题思路:
深度优先搜索的应用,代码很简洁,这个思想可以借鉴。
具体代码:
class Solution {
public int minDepth(TreeNode root) {
if(root == null)
return 0;
if(root.left != null && root.right != null)
return 1 + Math.min(minDepth(root.left), minDepth(root.right));
else
return 1 + minDepth(root.right) + minDepth(root.left);
}
}
运行结果:
例8 二叉树的最大宽度
题号:662,难度:中等(另外,可参考题号:543,二叉树的直径)
题目描述:
解题思路:
层次遍历的实际应用,依次更新每层最大宽度即可。
具体代码:
class Solution {
public int widthOfBinaryTree(TreeNode root) {
if(root == null)
return 0;
int result = 0;
Queue<TreeNode> queue = new LinkedList<>();
Queue<Integer> index = new LinkedList<>();
queue.offer(root);
index.offer(1);
while(queue.size() > 0) {
int count = queue.size();
int left = index.peek();
// System.out.println("left = "+left+", count = "+count);
while(count-- > 0) {
TreeNode temp = queue.poll();
int i = index.poll();
if(temp.left != null) {
queue.offer(temp.left);
index.offer(i * 2);
}
if(temp.right != null) {
queue.offer(temp.right);
index.offer(i * 2 + 1);
}
if(count == 0)
result = Math.max(result, 1 + i - left);
}
}
return result;
}
}
运行结果:
例9 依据前序和后序遍历构造二叉树
题号:889,难度:中等(另外,可参考同类型习题,题号:105,106,1008)
题目描述:
解题思路:
可以先手动构造画以下,体会其中的构造规则,然后采用深度优先搜索的思想来实现。每次找到当前子树的根节点,并确定左右子树的长度,并不断递归遍历构造即可。
具体代码:
class Solution {
private int[] pre;
private int[] post;
private Map<Integer, Integer> map; public TreeNode constructFromPrePost(int[] pre, int[] post) {
this.pre = pre;
this.post = post;
map = new HashMap<>();
for(int i = 0;i < post.length;i++)
map.put(post[i], i); return dfs(0, pre.length-1, 0, post.length-1);
} public TreeNode dfs(int pre_left, int pre_right, int post_left, int post_right) {
if(pre_left > pre_right || post_left > post_right)
return null;
TreeNode root = new TreeNode(pre[pre_left]);
int len = 0;
if(pre_left + 1 < pre_right)
len = map.get(pre[pre_left+1]) - post_left;
root.left = dfs(pre_left+1, pre_left+1+len < pre_right ? pre_left+1+len: pre_right, post_left, post_left+len);
root.right = dfs(pre_left+len+2, pre_right, post_left+len+1, post_right-1); return root;
}
}
运行结果:
例10 从先序遍历还原二叉树
题号:1028,难度:困难
题目描述:
解题思路:
定义一个全局变量用于确定当前深度优先遍历元素处在左子树还是右子树,能够有效减少代码量,并提高代码的可阅读性。
具体代码:
class Solution {
int i = 0; // 神来之笔, 定义全局变量i,可以有效区分左子树和右子树 public TreeNode recoverFromPreorder(String s) {
return buildtree(s,0);
} public TreeNode buildtree(String s,int depth){
if(i == s.length()) return null;
TreeNode cur = null;
int begin = i;
while(s.charAt(begin) == '-') begin ++;
int end = begin;
while(end < s.length() && s.charAt(end) - '0' >= 0 && s.charAt(end) - '0' < 10) end ++;
if(begin - i == depth){
cur = new TreeNode(Integer.valueOf(s.substring(begin,end)));
i = end;
}
if(cur != null){
// System.out.println("dep = "+depth+", cur = "+cur.val);
cur.left = buildtree(s,depth + 1);
cur.right = buildtree(s,depth + 1); // 通过全局变量i,可以在同一层深度找到右子树
}
return cur;
}
}
运行结果:
例11 二叉树的最近公共祖先
题号:236,难度:中等
题目描述:
解题思路:
此题一道和经典的面试题,代码量很少,但是对于很多初学者来说比较难以理解。采用深度优先搜索的思想,搜索目标节点。具体解题思路请参考代码。
具体代码:
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
// LCA 问题
if (root == null) {
return root;
}
if (root == p || root == q) {
return root;
}
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right = lowestCommonAncestor(root.right, p, q);
if (left != null && right != null) {
return root;
} else if (left != null) {
return left;
} else if (right != null) {
return right;
}
return null;
}
}
运行结果:
例12 打家劫舍 III
题号:337,难度:中等
题目描述:
解题思路:
本题考察后序遍历思想的应用,感觉外加了一点动态规划的思维。题目要求是寻找一个想加和较大的节点集。具体实现思路请参考代码。
具体代码:
class Solution { public int rob(TreeNode root) {
return postorder(root);
} public int postorder(TreeNode root){
if(root == null)
return 0;
postorder(root.left);
postorder(root.right);
int res1 = 0; // 左右
int res2 = root.val; //根
if (root.left != null){
res1 += root.left.val;
if (root.left.left != null)
res2 += root.left.left.val;
if (root.left.right != null)
res2 += root.left.right.val;
}
if (root.right != null){
res1 += root.right.val;
if (root.right.left != null)
res2 += root.right.left.val;
if (root.right.right!=null)
res2 += root.right.right.val;
}
root.val = Math.max(res1, res2);
return root.val;
}
}
运行结果:
例13 在二叉树中增加一行
题号:623,难度:中等
题目描述:
解题思路:
此题考察二叉树的添加节点的问题。并且保持原有节点的相对顺序不断,具体解题思路可参考代码。
具体代码:
class Solution {
public TreeNode addOneRow(TreeNode root, int v, int d) {
if (d == 0 || d == 1) {
TreeNode t = new TreeNode(v);
if (d == 1) t.left = root;
else t.right = root;
return t;
}
if (root != null && d > 1) {
root.left = addOneRow(root.left, v, d > 2 ? d - 1 : 1);
root.right = addOneRow(root.right, v, d > 2 ? d - 1 : 0);
}
return root;
}
}
运行结果:
例14 二叉树中所有距离为K的节点
题号:863,难度:中等
题目描述:
解题思路:
保存从根节点开始到叶子节点的每个路径,然后找到目标节点的位置,按照距离大小采用哈希定位的思想找到对应节点。
具体代码:
class Solution {
private Map<TreeNode,String>map=new HashMap<>();
private String path; public List<Integer> distanceK(TreeNode root, TreeNode target, int K) {
List<Integer>list=new ArrayList<>();
getNodeDist(root,target,"");
int i;
for(TreeNode key:map.keySet()){
String s=map.get(key);
for(i=0;i<s.length()&&i<path.length()&&s.charAt(i)==path.charAt(i);i++);
if(s.length()-i+path.length()-i==K)
list.add(key.val);
}
return list;
} public void getNodeDist(TreeNode root,TreeNode target,String p){
if(root != null){
path = root == target ? p : path;
map.put(root, p);
getNodeDist(root.left,target,p+"0");
getNodeDist(root.right,target,p+"1");
}
}
}
运行结果:
例15 监控二叉树
题号:968,难度:困难
题目描述:
解题思路:
此题也是选取一个符合题目要求的节点子集,但是取的要求是间隔化取点,并且需要满足数量最小。具体实现可参考下方代码。
具体代码:
class Solution {
private int ans = 0; public int minCameraCover(TreeNode root) {
if (root == null) return 0;
if (dfs(root) == 2) ans++;
return ans;
} // 1:该节点安装了监视器 2:该节点可观,但没有安装监视器 3:该节点不可观
private int dfs(TreeNode node) {
if (node == null)
return 1;
int left = dfs(node.left), right = dfs(node.right);
if (left == 2 || right == 2) {
ans++;
return 0;
} else if (left == 0 || right == 0){
return 1;
} else
return 2;
}
}
运行结果:
例16 二叉树着色游戏
题号:1145,难度:中等
题目描述:
解题思路:
此题也是一道节点选择的问题,但是涉及到了博弈论。按照题目的要求我们会发现选择一个节点后正常情况下会把整棵树分为三个部分,只需要获胜者能够访问的一部分节点个数大于另一方即可确保最终获胜。
具体代码:
class Solution {
//极客1选的起始点有多少个左节点
private int left = 0;
//极客1选的起始点有多少个右节点
private int right = 0; public boolean btreeGameWinningMove(TreeNode root, int n, int x) {
//极客1选了第一个节点后,将树划分为了三个部分(可能为空)
//第一部分:left 第二部分:right 第三部分:n - (left + right) - 1
//只需要总结点的数的一半 < 三个部分中的最大值,极客2就可以获胜
return getNum(root, x) / 2 < Math.max(Math.max(left, right), n - (left + right) - 1);
} private int getNum(TreeNode node, int x) {
if (node == null) {
return 0;
}
int r = getNum(node.right, x);
int l = getNum(node.left, x);
if (node.val == x) {
left = l;
right = r;
}
return l + r + 1;
}
}
运行结果:
例17 二叉树的所有路径
题号:257,难度:简单
题目描述:
解题思路:
此题是路径选择的一个基本习题,是解决路径相关问题的必须掌握的一道题。采用深度优先搜索保存每条路径即可。
具体代码:
class Solution { public List<String> binaryTreePaths(TreeNode root) {
List<String> ret = new ArrayList<>();
if(root==null)
return ret;
solve(root, "", ret);
return ret;
} public void solve(TreeNode root, String cur, List<String> ret){
if(root==null)
return;
cur += root.val;
if(root.left == null && root.right == null) {
ret.add(cur);
} else {
solve(root.left, cur+"->", ret);
solve(root.right, cur+"->", ret);
}
}
}
运行结果:
例18 二叉树中分配硬币
题号:979,难度:中等
题目描述:
解题思路:
本题考察我们采用前序遍历,并抽象为本题解答的过程。具体原理请参考代码。
具体代码:
class Solution {
/**
* 从后序遍历的第一个叶子节点开始,假设自己有x个金币,剩余x-1个金币都还给父节点,x-1可能为负数、0、正数
* x-1 < 0说明不够金币,需要从父节点获得,因此子节点有|x-1|个入方向的操作,次数加上|x-1|
* x-1 == 0说明刚好,无需与父节点有金币的交换,次数加0
* x-1 > 0 说明有多余的金币,需要交给父节点,因此子节点有x-1个出方向的操作,次数加上|x-1|
*/
private int ans = 0;// 移动次数
public int distributeCoins(TreeNode root) {
lrd(root);
return ans;
}
public int lrd(TreeNode root){
if(root == null){
return 0;
}
if(root.left != null){
root.val += lrd(root.left);
}
if(root.right != null){
root.val += lrd(root.right);
}
ans += Math.abs(root.val - 1);
return root.val - 1;
}
}
运行结果:
例19 二叉树的垂序遍历
题号:987,难度:中等
题目描述:
解题思路:
通过给每个节点定制编号的思路,采用前序遍历的思想来完成本题要求的垂序遍历。
具体代码:
class Solution {
private Map<Integer, List<List<Integer>>> map = new HashMap<>();
private int depth; public List<List<Integer>> verticalTraversal(TreeNode root) {
depth = getDepth(root);
dfs(root, 0, 0);
List<List<Integer>> result = new ArrayList<>();
int min = 0;
for(Integer key: map.keySet()){
min = Math.min(min, key);
result.add(new ArrayList<Integer>());
}
for(Integer key: map.keySet()){
for(int i = 0;i < depth;i++) {
List<Integer> temp = map.get(key).get(i);
if(temp.size() == 1)
result.get(key-min).add(temp.get(0));
else if(temp.size() > 1) { // 同层同列的元素,按照从小到大排序
Collections.sort(temp);
for(Integer t: temp)
result.get(key-min).add(t);
}
}
}
return result;
} public int getDepth(TreeNode root) {
if(root == null)
return 0;
return 1 + Math.max(getDepth(root.left), getDepth(root.right));
} public void dfs(TreeNode root, int x, int y) {
if(root == null)
return;
List<List<Integer>> temp;
if(map.containsKey(x))
temp = map.get(x);
else {
temp = new ArrayList<>();
for(int i = 0;i < depth;i++)
temp.add(new ArrayList<Integer>());
}
temp.get(y).add(root.val);
map.put(x, temp);
dfs(root.left, x-1, y+1);
dfs(root.right, x+1, y+1);
}
}
运行结果:
例20 二叉树中的最大路径和
题号:124,难度:困难
题目描述:
解题思路:
这道题的解题思路和例11 二叉树的最近公共祖先比较相似,都是采用深度优先搜索的思想,并分别寻找左右子树的结果,最后和根节点进行比较。具体实现的思路请参考下方代码。
具体代码:
class Solution { private int ret = Integer.MIN_VALUE; public int maxPathSum(TreeNode root) {
/**
对于任意一个节点, 如果最大和路径包含该节点, 那么只可能是两种情况:
1. 其左右子树中所构成的和路径值较大的那个加上该节点的值后向父节点回溯构成最大路径
2. 左右子树都在最大路径中, 加上该节点的值构成了最终的最大路径
**/
getMax(root);
return ret;
} private int getMax(TreeNode r) {
if(r == null) return 0;
int left = Math.max(0, getMax(r.left)); // 如果子树路径和为负则应当置0表示最大路径不包含子树
int right = Math.max(0, getMax(r.right));
ret = Math.max(ret, r.val + left + right); // 判断在该节点包含左右子树的路径和是否大于当前最大路径和
return Math.max(left, right) + r.val;
}
}
运行结果:
例21 路径总和 |||
题号:437,难度:简单
题目描述:
解题思路:
首先,此题并不简单。其次,本题是二叉树路径问题中一个很有代表性的问题。采用前序遍历的思想,以及根节点和子树的关系,不断更新最终结果。
具体代码:
class Solution {
int pathnumber;
public int pathSum(TreeNode root, int sum) {
if(root == null) return 0;
Sum(root,sum);
pathSum(root.left,sum);
pathSum(root.right,sum);
return pathnumber;
} public void Sum(TreeNode root, int sum){
if(root == null) return;
sum-=root.val;
if(sum == 0){
pathnumber++;
}
Sum(root.left,sum);
Sum(root.right,sum);
}
}
运行结果:
LeetCode刷题总结-树篇(上)的更多相关文章
- LeetCode刷题总结-树篇(下)
本文讲解有关树的习题中子树问题和新概念定义问题,也是有关树习题的最后一篇总结.前两篇请参考: LeetCode刷题总结-树篇(上) LeetCode刷题总结-树篇(中) 本文共收录9道题,7道中等题, ...
- LeetCode刷题总结-树篇(中)
本篇接着<LeetCode刷题总结-树篇(上)>,讲解有关树的类型相关考点的习题,本期共收录17道题,1道简单题,10道中等题,6道困难题. 在LeetCode题库中,考察到的不同种类的树 ...
- LeetCode刷题总结-数组篇(中)
本文接着上一篇文章<LeetCode刷题总结-数组篇(上)>,继续讲第二个常考问题:矩阵问题. 矩阵也可以称为二维数组.在LeetCode相关习题中,作者总结发现主要考点有:矩阵元素的遍历 ...
- LeetCode刷题总结-数组篇(上)
数组是算法中最常用的一种数据结构,也是面试中最常考的考点.在LeetCode题库中,标记为数组类型的习题到目前为止,已累计到了202题.然而,这202道习题并不是每道题只标记为数组一个考点,大部分习题 ...
- LeetCode刷题总结-数组篇(下)
本期讲O(n)类型问题,共14题.3道简单题,9道中等题,2道困难题.数组篇共归纳总结了50题,本篇是数组篇的最后一篇.其他三个篇章可参考: LeetCode刷题总结-数组篇(上),子数组问题(共17 ...
- LeetCode刷题专栏第一篇--思维导图&时间安排
昨天是元宵节,过完元宵节相当于这个年正式过完了.不知道大家有没有投入继续投入紧张的学习工作中.年前我想开一个Leetcode刷题专栏,于是发了一个投票想了解大家的需求征集意见.投票于2019年2月1日 ...
- C#LeetCode刷题-字典树
字典树篇 # 题名 刷题 通过率 难度 208 实现 Trie (前缀树) 48.6% 中等 211 添加与搜索单词 - 数据结构设计 39.9% 中等 212 单词搜索 II 27.9% ...
- LeetCode刷题总结-字符串篇
本文梳理对LeetCode上有关字符串习题的知识点,并给出对应的刷题建议.本文建议刷题的总数为32题.具体知识点如下图: 1.回文问题 题号:5. 最长回文子串,难度中等 题号:214. 最短回文串, ...
- C#LeetCode刷题-线段树
线段树篇 # 题名 刷题 通过率 难度 218 天际线问题 32.7% 困难 307 区域和检索 - 数组可修改 42.3% 中等 315 计算右侧小于当前元素的个数 31.9% 困难 4 ...
随机推荐
- 嵌入式、C语言位操作的一些技巧汇总
下面分享关于位操作的一些笔记: 一.位操作简单介绍 首先,以下是按位运算符: 在嵌入式编程中,常常需要对一些寄存器进行配置,有的情况下需要改变一个字节中的某一位或者几位,但是又不想改变其它位原有的值, ...
- 线程池ThreadPoolExecutor的使用方法
方法我们通过继承Thread类和实现runnable接口或者callable接口三种方式实现. 继承Thread类实际上也是实现了runnable接口,被继承的类主要是实现run()方法,通过star ...
- Linux LVM 配置
本文出自 “www.kisspuppet.com” 博客,请务必保留此出处http://dreamfire.blog.51cto.com/418026/1084729 许多Linux使用者安装操作系统 ...
- 2 JAVA语言的基本规则
1. 类名 类名需使用字母开头,使用驼峰命名法,如HelloWorld,对应的文件为 HelloWorld.java,与类名保持一致.编译好的字节码文件为 HelloWord.class. 2. 区分 ...
- django初始化
Django 版本 安装 pip安装 pip install django 安装最新版本的 pip install django==1.11.11 安装指定版本的 验证安装 直接去代码中调用djang ...
- 相关性不一定等于因果性:从 Yule-Simpson’s Paradox 讲起
1. 两件事伴随发生,不代表他们之间有因果关系 - 从一些荒诞相关性案例说起 在日常生活和数据分析中,我们可以得到大量相关性的结论,例如: 输入X变量,有98%置信度得到Y变量 只要努力,就能成功 只 ...
- 记录我的 python 学习历程-Day03 数据类型 str切片 for循环
一.啥是数据类型 我们人类可以很容易的分清数字与字符的区别,但是计算机并不能呀,计算机虽然很强大,但从某种角度上看又很傻,除非你明确的告诉它,1是数字,"汉"是文字,否则它是分 ...
- Composer安装和使用
Composer 是 PHP 的一个依赖管理工具.它允许你申明项目所依赖的代码库,它会在你的项目中为你安装他们.Composer 不是一个包管理器.是的,它涉及 "packages" ...
- ASCII, Unicode, UTF-8
(本文参考:http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html) 1. ASCII码 我们知道,在计算机内部,所有的 ...
- 十一次作业——LL(1)文法的判断,递归下降分析程序
1. 文法 G(S): (1)S -> AB (2)A ->Da|ε (3)B -> cC (4)C -> aADC |ε (5)D -> b|ε 验证文法 G(S)是不 ...