Binary Tree & Divide Conquer 完整笔记------part 1
目录:
- 前言
- 二叉树的3种遍历方式,前序遍历,中序遍历,后序遍历: preorderTraversalRec, preorderTraversal, inorderTraversalRec, postorderTraversalRec
- 求二叉树中的节点个数: getNodeNumRec(递归),getNodeNum(迭代)
- * 2. 求二叉树的深度: getDepthRec(递归),getDepth
- * 3. 前序遍历,中序遍历,后序遍历: preorderTraversalRec, preorderTraversal, inorderTraversalRec, postorderTraversalRec
- * (https://en.wikipedia.org/wiki/Tree_traversal#Pre-order_2)
- * 4.分层遍历二叉树(按层次从上往下,从左往右): levelTraversal, levelTraversalRec(递归解法!)
- * 5. 将二叉查找树变为有序的双向链表: convertBST2DLLRec, convertBST2DLL
- * 6. 求二叉树第K层的节点个数:getNodeNumKthLevelRec, getNodeNumKthLevel
- * 7. 求二叉树中叶子节点的个数:getNodeNumLeafRec, getNodeNumLeaf
- * 8. 判断两棵二叉树是否相同的树:isSameRec, isSame
- * 9. 判断二叉树是不是平衡二叉树:isAVLRec
- * 10. 求二叉树的镜像(破坏和不破坏原来的树两种情况):mirrorRec, mirrorCopyRec
- * 10.1 判断两个树是否互相镜像:isMirrorRec
- * 11. 求二叉树中两个节点的最低公共祖先节点:getLastCommonParent, getLastCommonParentRec, getLastCommonParentRec2
- * 12. 求二叉树中节点的最大距离:getMaxDistanceRec
- * 13. 由前序遍历序列和中序遍历序列重建二叉树:rebuildBinaryTreeRec
- * 14.判断二叉树是不是完全二叉树:isCompleteBinaryTree, isCompleteBinaryTreeRec
1. 前言
碰到二叉树的问题,就想想整棵树在该问题上的结果和左右子树在该问题上的结果之间的联系是什么!
举个🌰 :
Maximum Depth of Binary Tree
Given a binary tree, find its maximum depth.
The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node.
找这棵树的最大深度, 不是去traverse,bfs或者dfs。 想复杂了,太傻逼了
整棵树的最大深度 = max(左子树, 右子树) + 1; 简单到爆炸 = =
关于递归:
一个本质:把n的问题变成比n小的问题来解决
三个要素:出口 (对于二叉树问题,可能会把叶子节点作为出口,这样还得判断叶子节点是不是一开始就已经为空了,还不如直接递归到叶子节点的下一层,判断是否为空座位出口!)
----------------------------------------------------
2. bst3种遍历方式
* TODO: 一定要能熟练地写出所有问题的递归和非递归做法! 0.0
2. 1. 前序遍历
2. 1.1 前序遍历的非递归的方式
利用stack
while (!stack.isEmpty()) {
pop作为根节点;
根节点加入result list;
把右边节点加入到stack;
把左边节点加入到stack;
}
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public List<Integer> preorderTraversal(TreeNode root) { 3 Stack<TreeNode> stack = new Stack<TreeNode>(); 4 List<Integer> preorder = new ArrayList<Integer>(); 5 6 if (root == null) { 7 return preorder; 8 } 9 stack.push(root); 10 11 while(!stack.isEmpty()) { 12 TreeNode current = stack.pop(); 13 preorder.add(current.val); 14 if (current.right != null) { 15 stack.push(current.right); 16 } 17 if (current.left != null) { 18 stack.push(current.left); 19 } 20 } 21 return preorder; 22 } 23 }
2.1.2 前序遍历的Traverse 递归方式
traverse 到某个节点,把当前的root加入到result list里面, 然后去遍历它的左右儿子。
traverse方式把一个result储存结果的list在遍历的同时传递,一旦有新的点,就加入到result里面。
递归的function不用返回任何值,而是在function里面对传入的值进行更新处理。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public ArrayList<Integer> preorderTraversal(TreeNode root) { 3 ArrayList<Integer> result = new ArrayList<Integer>(); 4 traverse(root, result); 5 return result; 6 } 7 // 把root为跟的preorder加入result里面 8 private void traverse(TreeNode root, ArrayList<Integer> result) { 9 if (root == null) { 10 return; 11 } 12 13 result.add(root.val); 14 traverse(root.left, result); 15 traverse(root.right, result); 16 } 17 }
以上是traverse二叉树的代码
附上traverse图的version
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public List<Integer> preorderTraversal(Node root) { 3 List<Integer> result = new ArrayList<Integer>(); 4 if (root == null) { 5 return result; 6 } 7 traverse(result, root); 8 return result; 9 } 10 private void traverse(List<Integer> result, Node root) { 11 if (root == null) { 12 return; 13 } 14 //针对于有环的图 还需要visitied queue来判断是否已经visited 15 result.add(root.val); 16 Queue<Node> queue = new Queue<Node>(); 17 current = root.child; //假设child存在链表里 18 while (current != null) { 19 queue.add(current); 20 current = current.next; 21 } 22 23 24 for (Node current : stack) { 25 traverse(result, current); 26 27 } 28 } 29 }
2. 1.3 前序遍历的 divide and conquer方式
//todo
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 private static List<Integer> result; 3 public List<Integer> preorderTraversal(TreeNode root) { 4 result = new ArrayList<Integer>(); 5 if (root == null) { 6 return result; 7 } 8 helper (root); 9 return result; 10 } 11 private void helper(TreeNode root) { 12 if (root == null) { 13 return; 14 } 15 result.add(root.val); 16 helper(root.left); 17 helper(root.right); 18 } 19 }
-------------------------------------------------------------------------
2.2 中序遍历
2. 2.1 中序遍历的非递归方式
Using Stack is the obvious way to traverse tree without recursion. Below is an algorithm for traversing binary tree using stack. See this for step wise step execution of the algorithm.
1) Create an empty stack S. 2) Initialize current node as root 3) Push the current node to S and set current = current->left until current is NULL 4) If current is NULL and stack is not empty then a) Pop the top item from stack. b) Print the popped item, set current = popped_item->right c) Go to step 3. 5) If current is NULL and stack is empty then we are done.
Let us consider the below tree for example
1 / \ 2 3 / \ 4 5 Step 1 Creates an empty stack: S = NULL Step 2 sets current as address of root: current -> 1 Step 3 Pushes the current node and set current = current->left until current is NULL current -> 1 push 1: Stack S -> 1 current -> 2 push 2: Stack S -> 2, 1 current -> 4 push 4: Stack S -> 4, 2, 1 current = NULL Step 4 pops from S a) Pop 4: Stack S -> 2, 1 b) print "4" c) current = NULL /*right of 4 */ and go to step 3 Since current is NULL step 3 doesn't do anything. Step 4 pops again. a) Pop 2: Stack S -> 1 b) print "2" c) current -> 5/*right of 2 */ and go to step 3 Step 3 pushes 5 to stack and makes current NULL Stack S -> 5, 1 current = NULL Step 4 pops from S a) Pop 5: Stack S -> 1 b) print "5" c) current = NULL /*right of 5 */ and go to step 3 Since current is NULL step 3 doesn't do anything Step 4 pops again. a) Pop 1: Stack S -> NULL b) print "1" c) current -> 3 /*right of 5 */ Step 3 pushes 3 to stack and makes current NULL Stack S -> 3 current = NULL Step 4 pops from S a) Pop 3: Stack S -> NULL b) print "3" c) current = NULL /*right of 3 */ Traversal is done now as stack S is empty and current is NULL.
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public List<Integer> inorderTraversal(TreeNode root) { 3 Stack<TreeNode> stack = new Stack<TreeNode>(); 4 List<Integer> result = new ArrayList<Integer>(); 5 if (root == null) { 6 return result; 7 } 8 9 TreeNode current = root; 10 while (current != null || !stack.empty()) { 11 while (current != null) { 12 stack.push(current); 13 current = current.left; 14 } 15 current = stack.pop(); 16 result.add(current.val); 17 current = current.right; 18 } 19 return result; 20 } 21 }
2. 2.2 中序遍历的divide and conquer方式
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public List<Integer> inorderTraversal(TreeNode root) { 3 ArrayList<Integer> result = new ArrayList<Integer>(); 4 if (root == null) { 5 return result; 6 } 7 helper (root, result); 8 return result; 9 } 10 private TreeNode helper(TreeNode root, ArrayList<Integer> result) { 11 if (root == null) { 12 return null; 13 } 14 TreeNode current = new TreeNode(root.val); 15 TreeNode left = helper(root.left, result); 16 result.add(current.val); 17 TreeNode right = helper(root.right, result); 18 current.left = left; 19 current.right = right; 20 21 return current; 22 } 23 }
2.2.3 中序遍历traverse方式
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public List<Integer> inorderTraversal(TreeNode root) { 3 ArrayList<Integer> result = new ArrayList<Integer>(); 4 if (root == null) { 5 return result; 6 } 7 helper (root, result); 8 return result; 9 } 10 private void helper(TreeNode root, ArrayList<Integer> result) { 11 if (root == null) { 12 return; 13 } 14 helper(root.left, result); 15 result.add(root.val); 16 helper(root.right, result); 17 } 18 } 19
-------------------------------------------------------------------------
2.3 后序遍历
2.3.1 非递归方式 好醉 = =
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public ArrayList<Integer> postorderTraversal(TreeNode root) { 2 ArrayList<Integer> result = new ArrayList<Integer>(); 3 Stack<TreeNode> stack = new Stack<TreeNode>(); 4 TreeNode prev = null; // previously traversed node 5 TreeNode curr = root; 6 7 if (root == null) { 8 return result; 9 } 10 11 stack.push(root); 12 while (!stack.empty()) { 13 curr = stack.peek(); 14 if (prev == null || prev.left == curr || prev.right == curr) { // traverse down the tree 15 if (curr.left != null) { 16 stack.push(curr.left); 17 } else if (curr.right != null) { 18 stack.push(curr.right); 19 } 20 } else if (curr.left == prev) { // traverse up the tree from the left 21 if (curr.right != null) { 22 stack.push(curr.right); 23 } 24 } else { // traverse up the tree from the right 25 result.add(curr.val); 26 stack.pop(); 27 } 28 prev = curr; 29 } 30 31 return result; 32 }
------------------------------------------------------------------------------------------------------------------------------------------------------
3. 求二叉树的节点个数
考虑总的二叉树上问题的结果与左右子树上问题结果的关系
同样,总节点个数等于左右子树节点个数的总和 + 1(root)
递归的方法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * 求二叉树中的节点个数递归解法: O(n) 3 * (1)如果二叉树为空,节点个数为0 4 * (2)如果二叉树不为空,二叉树节点个数 = 左子树节点个数 + 5 * 右子树节点个数 + 1 6 */ 7 public static int getNodeNumRec(TreeNode root) { 8 if (root == null) { 9 return 0; 10 } else { 11 return getNodeNumRec(root.left) + getNodeNumRec(root.right) + 1; 12 } 13 } 14 15
非递归的方法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 2 /** 3 * 求二叉树中的节点个数迭代解法O(n):基本思想同LevelOrderTraversal, 4 * 即用一个Queue,在Java里面可以用LinkedList来模拟 5 */ 6 public static int getNodeNum(TreeNode root) { 7 if(root == null){ 8 return 0; 9 } 10 int count = 1; 11 Queue<TreeNode> queue = new LinkedList<TreeNode>(); 12 queue.add(root); 13 14 while(!queue.isEmpty()){ 15 TreeNode cur = queue.remove(); // 从队头位置移除 16 if(cur.left != null){ // 如果有左孩子,加到队尾 17 queue.add(cur.left); 18 count++; 19 } 20 if(cur.right != null){ // 如果有右孩子,加到队尾 21 queue.add(cur.right); 22 count++; 23 } 24 } 25 26 return count; 27 } 28
----------------------------------------------------------------------
4. 求二叉树的高度
递归解法: O(n)
- * (1)如果二叉树为空,二叉树的深度为0
- * (2)如果二叉树不为空,二叉树的深度 = max(左子树深度, 右子树深度) + 1
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public static int getDepthRec(TreeNode root) { 2 if (root == null) { 3 return 0; 4 } 5 6 int leftDepth = getDepthRec(root.left); 7 int rightDepth = getDepthRec(root.right); 8 return Math.max(leftDepth, rightDepth) + 1; 9 }
非递归解法:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * 求二叉树的深度(高度) 迭代解法: O(n) 3 * 基本思想同LevelOrderTraversal,还是用一个Queue 4 */ 5 public static int getDepth(TreeNode root) { 6 if(root == null){ 7 return 0; 8 } 9 10 int depth = 0; // 深度 11 int currentLevelNodes = 1; // 当前Level,node的数量 12 int nextLevelNodes = 0; // 下一层Level,node的数量 13 14 LinkedList<TreeNode> queue = new LinkedList<TreeNode>(); 15 queue.add(root); 16 17 while( !queue.isEmpty() ){ 18 TreeNode cur = queue.remove(); // 从队头位置移除 19 currentLevelNodes--; // 减少当前Level node的数量 20 if(cur.left != null){ // 如果有左孩子,加到队尾 21 queue.add(cur.left); 22 nextLevelNodes++; // 并增加下一层Level node的数量 23 } 24 if(cur.right != null){ // 如果有右孩子,加到队尾 25 queue.add(cur.right); 26 nextLevelNodes++; 27 } 28 29 if(currentLevelNodes == 0){ // 说明已经遍历完当前层的所有节点 30 depth++; // 增加高度 31 currentLevelNodes = nextLevelNodes; // 初始化下一层的遍历 32 nextLevelNodes = 0; 33 } 34 } 35 36 return depth; 37 }
当求二叉树的最小高度时,增加一个判断left和right同时为空的时候,返回
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 public class Solution { 11 public int minDepth(TreeNode root) { 12 if (root == null) { 13 return 0; 14 } 15 int currentLvlNodes = 1; 16 int nextLvlNodes = 0; 17 int maxDepth = 0; 18 Queue<TreeNode> queue = new LinkedList<TreeNode>(); 19 queue.add(root); 20 while (!queue.isEmpty()) { 21 TreeNode current = queue.remove(); 22 currentLvlNodes--; 23 if (current.left == null && current.right == null) { 24 return ++maxDepth; 25 } 26 if (current.left != null) { 27 queue.add(current.left); 28 nextLvlNodes++; 29 } 30 if (current.right != null) { 31 queue.add(current.right); 32 nextLvlNodes++; 33 } 34 35 if (currentLvlNodes == 0) { 36 maxDepth++; 37 currentLvlNodes = nextLvlNodes; 38 nextLvlNodes = 0; 39 } 40 } 41 return maxDepth; 42 } 43 }
-------------------------------------------------------------------------
5. 求二叉树的分层遍历
Given a binary tree, return the level order traversal of its nodes' values. (ie, from left to right, level by level).
For example:
Given binary tree [3,9,20,null,null,15,7]
,
3 / \ 9 20 / \ 15 7
return its level order traversal as:
[ [3], [9,20], [15,7] ]
最显然分层搜索,利用bfs的方法。 模仿二叉树高度的方法,用2个变量分别记录当前层的节点数,当当前层的节点数为0时,表示完成了当前层的遍历,把当前层的结果加入到result里面。
由于引入了当前层节点的数量,下一层节点的数量。这样什么时候更新这个数量,拿哪个数量作为控制条件很重要。记住在小循环最后跳出去之前要重置next level node number。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition for a binary tree node. 3 * public class TreeNode { 4 * int val; 5 * TreeNode left; 6 * TreeNode right; 7 * TreeNode(int x) { val = x; } 8 * } 9 */ 10 public class Solution { 11 public List<List<Integer>> levelOrder(TreeNode root) { 12 List<List<Integer>> result = new ArrayList<List<Integer>>(); 13 if (root == null) { 14 return result; 15 } 16 17 Queue<TreeNode> queue = new LinkedList<TreeNode>(); 18 queue.add(root); 19 int currentLvlNodes = 1; 20 int nextLvlNodes = 0; 21 22 23 while (currentLvlNodes != 0) {// 新的一层的遍历的开始 24 List<Integer> temp = new ArrayList<Integer>(); 25 26 while (!queue.isEmpty()) { 27 TreeNode current = queue.remove(); 28 temp.add(current.val); 29 currentLvlNodes--; 30 if (current.left != null) { 31 queue.add(current.left); 32 nextLvlNodes++; 33 } 34 if (current.right != null) { 35 queue.add(current.right); 36 nextLvlNodes++; 37 } 38 39 if (currentLvlNodes == 0) { //finish current level traverse and need to break to next level. remember to update nextLvlNodes number 40 currentLvlNodes = nextLvlNodes; 41 nextLvlNodes = 0; 42 result.add(temp); 43 break; 44 } 45 46 } 47 } 48 return result; 49 } 50 }
九章的bfs,发现其实level node number 就隐含在queue里面。用size去拿到就好了。 熟悉的配方,熟悉的套路
while 大循环
for ( < size ) 控制小循环
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public class Solution { 2 public ArrayList<ArrayList<Integer>> levelOrder(TreeNode root) { 3 ArrayList result = new ArrayList(); 4 5 if (root == null) { 6 return result; 7 } 8 9 Queue<TreeNode> queue = new LinkedList<TreeNode>(); 10 queue.offer(root); 11 12 while (!queue.isEmpty()) { 13 ArrayList<Integer> level = new ArrayList<Integer>(); 14 int size = queue.size(); 15 for (int i = 0; i < size; i++) { 16 TreeNode head = queue.poll(); 17 level.add(head.val); 18 if (head.left != null) { 19 queue.offer(head.left); 20 } 21 if (head.right != null) { 22 queue.offer(head.right); 23 } 24 } 25 result.add(level); 26 } 27 28 return result; 29 } 30 }
另外还有 dfs方法,two queue方法, queue with dummy node方法
http://www.jiuzhang.com/solutions/binary-tree-level-order-traversal/
-------------------------------------------------------------------------
6. Convert Binary Search Tree to Doubly Linked List
Convert a binary search tree to doubly linked list with in-order traversal.
Given a binary search tree:
4
/ \
2 5
/ \
1 3
return 1<->2<->3<->4<->5
版本1: 不能新建节点,只能调整node指针的版本,递归方式
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * 将二叉查找树变为有序的双向链表 要求不能创建新节点,只调整指针。 3 * 递归解法: 4 * 参考了http://stackoverflow.com/questions/11511898/converting-a-binary-search-tree-to-doubly-linked-list#answer-11530016 5 * 感觉是最清晰的递归解法,但要注意递归完,root会在链表的中间位置,因此要手动 6 * 把root移到链表头或链表尾 7 */ 8 public static TreeNode convertBST2DLLRec(TreeNode root) { 9 root = convertBST2DLLSubRec(root); 10 11 // root会在链表的中间位置,因此要手动把root移到链表头 12 while(root.left != null){ 13 root = root.left; 14 } 15 return root; 16 } 17 18 /** 19 * 递归转换BST为双向链表(DLL) 20 */ 21 public static TreeNode convertBST2DLLSubRec(TreeNode root){ 22 if(root==null || (root.left==null && root.right==null)){ 23 return root; 24 } 25 26 TreeNode tmp = null; 27 if(root.left != null){ // 处理左子树 28 tmp = convertBST2DLLSubRec(root.left); 29 while(tmp.right != null){ // 寻找最右节点 30 tmp = tmp.right; 31 } 32 tmp.right = root; // 把左子树处理后结果和root连接 33 root.left = tmp; 34 } 35 if(root.right != null){ // 处理右子树 36 tmp = convertBST2DLLSubRec(root.right); 37 while(tmp.left != null){ // 寻找最左节点 38 tmp = tmp.left; 39 } 40 tmp.left = root; // 把右子树处理后结果和root连接 41 root.right = tmp; 42 } 43 return root; 44 }
版本2: 只能调整node指针的非递归版本
类似于中序遍历
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * 将二叉查找树变为有序的双向链表 迭代解法 3 // * 类似inorder traversal的做法 4 */ 5 public static TreeNode convertBST2DLL(TreeNode root) { 6 if(root == null){ 7 return null; 8 } 9 Stack<TreeNode> stack = new Stack<TreeNode>(); 10 TreeNode cur = root; // 指向当前处理节点 11 TreeNode old = null; // 指向前一个处理的节点 12 TreeNode head = null; // 链表头 13 14 while( true ){ 15 while(cur != null){ // 先添加一个非空节点所有的左孩子到栈 16 stack.push(cur); 17 cur = cur.left; 18 } 19 20 if(stack.isEmpty()){ 21 break; 22 } 23 24 // 因为此时已经没有左孩子了,所以输出栈顶元素 25 cur = stack.pop(); 26 if(old != null){ 27 old.right = cur; 28 } 29 if(head == null){ // /第一个节点为双向链表头节点 30 head = cur; 31 } 32 33 old = cur; // 更新old 34 cur = cur.right; // 准备处理右子树 35 } 36 37 return head; 38 }
需要新建节点的版本:
版本3: 中序遍历的思想,把左子树的末尾和当前node相连, 把右子树的头和当前node相连
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 /** 2 * Definition of TreeNode: 3 * public class TreeNode { 4 * public int val; 5 * public TreeNode left, right; 6 * public TreeNode(int val) { 7 * this.val = val; 8 * this.left = this.right = null; 9 * } 10 * } 11 * Definition for Doubly-ListNode. 12 * public class DoublyListNode { 13 * int val; 14 * DoublyListNode next, prev; 15 * DoublyListNode(int val) { 16 * this.val = val; 17 * this.next = this.prev = null; 18 * } 19 * } 20 */ 21 public class Solution { 22 /** 23 * @param root: The root of tree 24 * @return: the head of doubly list node 25 */ 26 public DoublyListNode bstToDoublyList(TreeNode root) { 27 if (root == null) { 28 return null; 29 } 30 DoublyListNode result = helper(root).first; 31 return result; 32 } 33 34 public ResultType helper(TreeNode root) { 35 if (root == null) { 36 return null; 37 } 38 ResultType left = helper(root.left); 39 ResultType right = helper(root.right); 40 DoublyListNode curt = new DoublyListNode(root.val); 41 ResultType curtRes = new ResultType(null, null); 42 43 if (left == null) { 44 curtRes.first = curt; 45 } else { 46 curtRes.first = left.first; 47 curt.prev = left.last; 48 left.last.next = curt; 49 } 50 51 if (right == null) { 52 curtRes.last = curt; 53 } else { 54 curtRes.last = right.last; 55 curt.next = right.first; 56 right.first.prev = curt; 57 } 58 return curtRes; 59 } 60 } 61 62 63 class ResultType { 64 DoublyListNode first, last; 65 public ResultType(DoublyListNode fi, DoublyListNode la) { 66 first = fi; 67 last = la; 68 } 69 }
-------------------------------------------------------------------------------------------------------------------------------------------------------------
7. 求二叉树的k层节点的个数
递归的解法: 注意是从root开始,每往下递归一层,k--
target即目标层始终不变,变的是把哪一层的点当作root
具体分为3种情况
(1)如果二叉树为空或者k<1返回0
(2)如果二叉树不为空并且k==1,返回1
(3)如果二叉树不为空且k>1,返回root左子树中k-1层的节点个数与root右子树k-1层节点个数之和
求以root为根的k层节点数目 等价于
求以root左孩子为根的k-1层(因为少了root那一层)节点数目 加上
以root右孩子为根的k-1层(因为少了root那一层)节点数目
所以遇到树,先把它拆成左子树和右子树,把问题降解
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public static int getNodeNumKthLevelRec(TreeNode root, int k) { 2 if (root == null || k < 1) { 3 return 0; 4 } 5 6 if (k == 1) { 7 return 1; 8 } 9 int numLeft = getNodeNumKthLevelRec(root.left, k - 1); // 求root左子树的k-1层节点数 10 int numRight = getNodeNumKthLevelRec(root.right, k - 1); // 求root右子树的k-1层节点数 11 return numLeft + numRight; 12 }
对于非递归,和level order 一样,到达第k层的时候,直接返回当前层的node number
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public static int getNodeNumKthLevel(TreeNode root, int k){ 2 if(root == null){ 3 return 0; 4 } 5 Queue<TreeNode> queue = new LinkedList<TreeNode>(); 6 queue.add(root); 7 8 int i = 1; 9 int currentLevelNodes = 1; // 当前Level,node的数量 10 int nextLevelNodes = 0; // 下一层Level,node的数量 11 12 while( !queue.isEmpty() && i<k){ 13 TreeNode cur = queue.remove(); // 从队头位置移除 14 currentLevelNodes--; // 减少当前Level node的数量 15 if(cur.left != null){ // 如果有左孩子,加到队尾 16 queue.add(cur.left); 17 nextLevelNodes++; // 并增加下一层Level node的数量 18 } 19 if(cur.right != null){ // 如果有右孩子,加到队尾 20 queue.add(cur.right); 21 nextLevelNodes++; 22 } 23 24 if(currentLevelNodes == 0){ // 说明已经遍历完当前层的所有节点 25 currentLevelNodes = nextLevelNodes; // 初始化下一层的遍历 26 nextLevelNodes = 0; 27 i++; // 进入到下一层 28 } 29 } 30 31 return currentLevelNodes; 32 }
-------------------------------------------------------------------------
8. 求叶子节点的个数
递归: 总的叶子节点个数 = 左子树叶子节点个数+ 右子树叶子节点的个数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public static int getNodeNumLeafRec(TreeNode root) { 2 // 当root不存在,返回空 3 if (root == null) { 4 return 0; 5 } 6 7 // 当为叶子节点时返回1 8 if (root.left == null && root.right == null) { 9 return 1; 10 } 11 12 // 把一个树拆成左子树和右子树之和,原理同上一题 13 return getNodeNumLeafRec(root.left) + getNodeNumLeafRec(root.right); 14 }
非递归:
基于level traverse,当一个节点的左右节点都为空时,即为叶子, 计数器++
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 public static int getNodeNumLeaf(TreeNode root) { 2 if(root == null){ 3 return 0; 4 } 5 Queue<TreeNode> queue = new LinkedList<TreeNode>(); 6 queue.add(root); 7 8 int leafNodes = 0; // 记录上一个Level,node的数量 9 10 while( !queue.isEmpty() ){ 11 TreeNode cur = queue.remove(); // 从队头位置移除 12 if(cur.left != null){ // 如果有左孩子,加到队尾 13 queue.add(cur.left); 14 } 15 if(cur.right != null){ // 如果有右孩子,加到队尾 16 queue.add(cur.right); 17 } 18 if(cur.left==null && cur.right==null){ // 叶子节点 19 leafNodes++; 20 } 21 } 22 23 return leafNodes; 24 }
-------------------------------------------------------------------------