面试中关于二叉树的问题(Java版)

  1. package org.hunan.guan.javaIO;  
  2.   
  3. import java.util.ArrayList;  
  4. import java.util.Iterator;  
  5. import java.util.LinkedList;  
  6. import java.util.List;  
  7. import java.util.Queue;  
  8. import java.util.Stack;  
  9.   
  10. /** 
  11.  * http://blog.csdn.net/luckyxiaoqiang/article/details/7518888  轻松搞定面试中的二叉树题目 
  12.  * http://www.cnblogs.com/Jax/archive/2009/12/28/1633691.html  算法大全(3) 二叉树 
  13.  *  
  14.  * TODO: 一定要能熟练地写出所有问题的递归和非递归做法! 
  15.  * 
  16.  * 1. 求二叉树中的节点个数: getNodeNumRec(递归),getNodeNum(迭代) 
  17.  * 2. 求二叉树的深度: getDepthRec(递归),getDepth  
  18.  * 3. 前序遍历,中序遍历,后序遍历: preorderTraversalRec, preorderTraversal, inorderTraversalRec, postorderTraversalRec 
  19.  * (https://en.wikipedia.org/wiki/Tree_traversal#Pre-order_2) 
  20.  * 4.分层遍历二叉树(按层次从上往下,从左往右): levelTraversal, levelTraversalRec(递归解法!) 
  21.  * 5. 将二叉查找树变为有序的双向链表: convertBST2DLLRec, convertBST2DLL 
  22.  * 6. 求二叉树第K层的节点个数:getNodeNumKthLevelRec, getNodeNumKthLevel 
  23.  * 7. 求二叉树中叶子节点的个数:getNodeNumLeafRec, getNodeNumLeaf 
  24.  * 8. 判断两棵二叉树是否相同的树:isSameRec, isSame 
  25.  * 9. 判断二叉树是不是平衡二叉树:isAVLRec 
  26.  * 10. 求二叉树的镜像(破坏和不破坏原来的树两种情况):mirrorRec, mirrorCopyRec 
  27.  * 10.1 判断两个树是否互相镜像:isMirrorRec 
  28.  * 11. 求二叉树中两个节点的最低公共祖先节点:getLastCommonParent, getLastCommonParentRec, getLastCommonParentRec2 
  29.  * 12. 求二叉树中节点的最大距离:getMaxDistanceRec 
  30.  * 13. 由前序遍历序列和中序遍历序列重建二叉树:rebuildBinaryTreeRec 
  31.  * 14.判断二叉树是不是完全二叉树:isCompleteBinaryTree, isCompleteBinaryTreeRec 
  32.  *  
  33.  */  
  34. public class BinaryTreeSummary {  
  35.   
  36.     /* 
  37.                  1  
  38.                 / \  
  39.                2   3  
  40.               / \   \  
  41.              4  5   6  
  42.      */  
  43.     public static void main(String[] args) {  
  44.         TreeNode r1 = new TreeNode(1);  
  45.         TreeNode r2 = new TreeNode(2);  
  46.         TreeNode r3 = new TreeNode(3);  
  47.         TreeNode r4 = new TreeNode(4);  
  48.         TreeNode r5 = new TreeNode(5);  
  49.         TreeNode r6 = new TreeNode(6);  
  50.           
  51.         r1.left = r2;  
  52.         r1.right = r3;  
  53.         r2.left = r4;  
  54.         r2.right = r5;  
  55.         r3.right = r6;  
  56.           
  57. //      System.out.println(getNodeNumRec(r1));  
  58. //      System.out.println(getNodeNum(r1));  
  59. //      System.out.println(getDepthRec(r1));  
  60. //      System.out.println(getDepth(r1));  
  61.           
  62. //      preorderTraversalRec(r1);  
  63. //      System.out.println();  
  64. //      preorderTraversal(r1);  
  65. //      System.out.println();  
  66. //      inorderTraversalRec(r1);  
  67. //      System.out.println();  
  68. //      inorderTraversal(r1);  
  69. //      System.out.println();  
  70. //      postorderTraversalRec(r1);  
  71. //      System.out.println();  
  72. //      postorderTraversal(r1);  
  73. //      System.out.println();  
  74. //      levelTraversal(r1);  
  75. //      System.out.println();  
  76. //      levelTraversalRec(r1);  
  77. //      System.out.println();  
  78.           
  79. //      TreeNode tmp = convertBSTRec(r1);  
  80. //      while(true){  
  81. //          if(tmp == null){  
  82. //              break;  
  83. //          }  
  84. //          System.out.print(tmp.val + " ");  
  85. //          if(tmp.right == null){  
  86. //              break;  
  87. //          }  
  88. //          tmp = tmp.right;  
  89. //      }  
  90. //      System.out.println();  
  91. //      while(true){  
  92. //          if(tmp == null){  
  93. //              break;  
  94. //          }  
  95. //          System.out.print(tmp.val + " ");  
  96. //          if(tmp.left == null){  
  97. //              break;  
  98. //          }  
  99. //          tmp = tmp.left;  
  100. //      }  
  101.           
  102.           
  103. //      TreeNode tmp = convertBST2DLL(r1);  
  104. //      while(true){  
  105. //          if(tmp == null){  
  106. //              break;  
  107. //          }  
  108. //          System.out.print(tmp.val + " ");  
  109. //          if(tmp.right == null){  
  110. //              break;  
  111. //          }  
  112. //          tmp = tmp.right;  
  113. //      }  
  114.           
  115. //      System.out.println(getNodeNumKthLevelRec(r1, 2));  
  116. //      System.out.println(getNodeNumKthLevel(r1, 2));  
  117.           
  118. //      System.out.println(getNodeNumLeafRec(r1));  
  119. //      System.out.println(getNodeNumLeaf(r1));  
  120.           
  121. //      System.out.println(isSame(r1, r1));  
  122. //      inorderTraversal(r1);  
  123. //      System.out.println();  
  124. //      mirror(r1);  
  125. //      TreeNode mirrorRoot = mirrorCopy(r1);  
  126. //      inorderTraversal(mirrorRoot);  
  127.           
  128.         System.out.println(isCompleteBinaryTree(r1));  
  129.         System.out.println(isCompleteBinaryTreeRec(r1));  
  130.           
  131.     }  
  132.   
  133.     private static class TreeNode {  
  134.         int val;  
  135.         TreeNode left;  
  136.         TreeNode right;  
  137.   
  138.         public TreeNode(int val) {  
  139.             this.val = val;  
  140.         }  
  141.     }  
  142.   
  143.     /** 
  144.      * 求二叉树中的节点个数递归解法: O(n) 
  145.      * (1)如果二叉树为空,节点个数为0  
  146.      * (2)如果二叉树不为空,二叉树节点个数 = 左子树节点个数 + 
  147.      *            右子树节点个数 + 1 
  148.      */  
  149.     public static int getNodeNumRec(TreeNode root) {  
  150.         if (root == null) {  
  151.             return 0;  
  152.         } else {  
  153.             return getNodeNumRec(root.left) + getNodeNumRec(root.right) + 1;  
  154.         }  
  155.     }  
  156.       
  157.     /** 
  158.      *  求二叉树中的节点个数迭代解法O(n):基本思想同LevelOrderTraversal, 
  159.      *  即用一个Queue,在Java里面可以用LinkedList来模拟  
  160.      */  
  161.     public static int getNodeNum(TreeNode root) {  
  162.         if(root == null){  
  163.             return 0;  
  164.         }  
  165.         int count = 1;  
  166.         Queue<TreeNode> queue = new LinkedList<TreeNode>();  
  167.         queue.add(root);  
  168.           
  169.         while(!queue.isEmpty()){  
  170.             TreeNode cur = queue.remove();      // 从队头位置移除  
  171.             if(cur.left != null){           // 如果有左孩子,加到队尾  
  172.                 queue.add(cur.left);  
  173.                 count++;  
  174.             }  
  175.             if(cur.right != null){      // 如果有右孩子,加到队尾  
  176.                 queue.add(cur.right);  
  177.                 count++;  
  178.             }  
  179.         }  
  180.           
  181.         return count;  
  182.     }  
  183.   
  184.     /** 
  185.      * 求二叉树的深度(高度) 递归解法: O(n) 
  186.      * (1)如果二叉树为空,二叉树的深度为0  
  187.      * (2)如果二叉树不为空,二叉树的深度 = max(左子树深度, 右子树深度) + 1 
  188.      */  
  189.     public static int getDepthRec(TreeNode root) {  
  190.         if (root == null) {  
  191.             return 0;  
  192.         }  
  193.   
  194.         int leftDepth = getDepthRec(root.left);  
  195.         int rightDepth = getDepthRec(root.right);  
  196.         return Math.max(leftDepth, rightDepth) + 1;  
  197.     }  
  198.       
  199.     /** 
  200.      * 求二叉树的深度(高度) 迭代解法: O(n) 
  201.      * 基本思想同LevelOrderTraversal,还是用一个Queue 
  202.      */  
  203.     public static int getDepth(TreeNode root) {  
  204.         if(root == null){  
  205.             return 0;  
  206.         }  
  207.           
  208.         int depth = 0;                          // 深度  
  209.         int currentLevelNodes = 1;      // 当前Level,node的数量  
  210.         int nextLevelNodes = 0;         // 下一层Level,node的数量  
  211.           
  212.         LinkedList<TreeNode> queue = new LinkedList<TreeNode>();  
  213.         queue.add(root);  
  214.           
  215.         while( !queue.isEmpty() ){  
  216.             TreeNode cur = queue.remove();      // 从队头位置移除  
  217.             currentLevelNodes--;            // 减少当前Level node的数量  
  218.             if(cur.left != null){               // 如果有左孩子,加到队尾  
  219.                 queue.add(cur.left);  
  220.                 nextLevelNodes++;           // 并增加下一层Level node的数量  
  221.             }  
  222.             if(cur.right != null){          // 如果有右孩子,加到队尾  
  223.                 queue.add(cur.right);  
  224.                 nextLevelNodes++;  
  225.             }  
  226.               
  227.             if(currentLevelNodes == 0){ // 说明已经遍历完当前层的所有节点  
  228.                 depth++;                       // 增加高度  
  229.                 currentLevelNodes = nextLevelNodes;     // 初始化下一层的遍历  
  230.                 nextLevelNodes = 0;  
  231.             }  
  232.         }  
  233.           
  234.         return depth;  
  235.     }  
  236.       
  237.       
  238.   
  239.     /** 
  240.      * 前序遍历,中序遍历,后序遍历 前序遍历递归解法:  
  241.      * (1)如果二叉树为空,空操作  
  242.      * (2)如果二叉树不为空,访问根节点,前序遍历左子树,前序遍历右子树 
  243.      */  
  244.     public static void preorderTraversalRec(TreeNode root) {  
  245.         if (root == null) {  
  246.             return;  
  247.         }  
  248.         System.out.print(root.val + " ");  
  249.         preorderTraversalRec(root.left);  
  250.         preorderTraversalRec(root.right);  
  251.     }  
  252.       
  253.     /** 
  254.      *  前序遍历迭代解法:用一个辅助stack,总是把右孩子放进栈 
  255.      *  http://www.youtube.com/watch?v=uPTCbdHSFg4 
  256.      */  
  257.     public static void preorderTraversal(TreeNode root) {  
  258.         if(root == null){  
  259.             return;  
  260.         }  
  261.           
  262.         Stack<TreeNode> stack = new Stack<TreeNode>();      // 辅助stack  
  263.         stack.push(root);  
  264.           
  265.         while( !stack.isEmpty() ){  
  266.             TreeNode cur = stack.pop();     // 出栈栈顶元素  
  267.             System.out.print(cur.val + " ");  
  268.               
  269.             // 关键点:要先压入右孩子,再压入左孩子,这样在出栈时会先打印左孩子再打印右孩子  
  270.             if(cur.right != null){  
  271.                 stack.push(cur.right);  
  272.             }  
  273.             if(cur.left != null){  
  274.                 stack.push(cur.left);  
  275.             }  
  276.         }  
  277.     }  
  278.   
  279.     /** 
  280.      * 中序遍历递归解法  
  281.      * (1)如果二叉树为空,空操作。  
  282.      * (2)如果二叉树不为空,中序遍历左子树,访问根节点,中序遍历右子树 
  283.      */  
  284.     public static void inorderTraversalRec(TreeNode root) {  
  285.         if (root == null) {  
  286.             return;  
  287.         }  
  288.         inorderTraversalRec(root.left);  
  289.         System.out.print(root.val + " ");  
  290.         inorderTraversalRec(root.right);  
  291.     }  
  292.       
  293.     /** 
  294.      * 中序遍历迭代解法 ,用栈先把根节点的所有左孩子都添加到栈内, 
  295.      * 然后输出栈顶元素,再处理栈顶元素的右子树 
  296.      * http://www.youtube.com/watch?v=50v1sJkjxoc 
  297.      *  
  298.      * 还有一种方法能不用递归和栈,基于线索二叉树的方法,较麻烦以后补上 
  299.      * http://www.geeksforgeeks.org/inorder-tree-traversal-without-recursion-and-without-stack/ 
  300.      */  
  301.     public static void inorderTraversal(TreeNode root){  
  302.         if(root == null){  
  303.             return;  
  304.         }  
  305.         Stack<TreeNode> stack = new Stack<TreeNode>();  
  306.         TreeNode cur = root;  
  307.           
  308.         whiletrue ){  
  309.             while(cur != null){     // 先添加一个非空节点所有的左孩子到栈  
  310.                 stack.push(cur);  
  311.                 cur = cur.left;  
  312.             }  
  313.               
  314.             if(stack.isEmpty()){  
  315.                 break;  
  316.             }  
  317.                   
  318.             // 因为此时已经没有左孩子了,所以输出栈顶元素  
  319.             cur = stack.pop();  
  320.             System.out.print(cur.val + " ");  
  321.             cur = cur.right;    // 准备处理右子树  
  322.         }  
  323.     }  
  324.   
  325.     /** 
  326.      * 后序遍历递归解法  
  327.      * (1)如果二叉树为空,空操作  
  328.      * (2)如果二叉树不为空,后序遍历左子树,后序遍历右子树,访问根节点 
  329.      */  
  330.     public static void postorderTraversalRec(TreeNode root) {  
  331.         if (root == null) {  
  332.             return;  
  333.         }  
  334.         postorderTraversalRec(root.left);  
  335.         postorderTraversalRec(root.right);  
  336.         System.out.print(root.val + " ");  
  337.     }  
  338.       
  339.     /** 
  340.      *  后序遍历迭代解法 
  341.      *  http://www.youtube.com/watch?v=hv-mJUs5mvU 
  342.      *   
  343.      */  
  344.     public static void postorderTraversal(TreeNode root) {  
  345.         if (root == null) {  
  346.             return;  
  347.         }  
  348.           
  349.         Stack<TreeNode> s = new Stack<TreeNode>();      // 第一个stack用于添加node和它的左右孩子  
  350.         Stack<TreeNode> output = new Stack<TreeNode>();// 第二个stack用于翻转第一个stack输出  
  351.           
  352.         s.push(root);  
  353.         while( !s.isEmpty() ){      // 确保所有元素都被翻转转移到第二个stack  
  354.             TreeNode cur = s.pop(); // 把栈顶元素添加到第二个stack  
  355.             output.push(cur);         
  356.               
  357.             if(cur.left != null){       // 把栈顶元素的左孩子和右孩子分别添加入第一个stack  
  358.                 s.push(cur.left);  
  359.             }  
  360.             if(cur.right != null){  
  361.                 s.push(cur.right);  
  362.             }  
  363.         }  
  364.           
  365.         while( !output.isEmpty() ){ // 遍历输出第二个stack,即为后序遍历  
  366.             System.out.print(output.pop().val + " ");  
  367.         }  
  368.     }  
  369.   
  370.     /** 
  371.      * 分层遍历二叉树(按层次从上往下,从左往右)迭代 
  372.      * 相当于广度优先搜索,使用队列实现。队列初始化,将根节点压入队列。当队列不为空,进行如下操作:弹出一个节点 
  373.      * ,访问,若左子节点或右子节点不为空,将其压入队列 
  374.      */  
  375.     public static void levelTraversal(TreeNode root) {  
  376.         if (root == null) {  
  377.             return;  
  378.         }  
  379.         LinkedList<TreeNode> queue = new LinkedList<TreeNode>();  
  380.         queue.push(root);  
  381.   
  382.         while (!queue.isEmpty()) {  
  383.             TreeNode cur = queue.removeFirst();  
  384.             System.out.print(cur.val + " ");  
  385.             if (cur.left != null) {  
  386.                 queue.add(cur.left);  
  387.             }  
  388.             if (cur.right != null) {  
  389.                 queue.add(cur.right);  
  390.             }  
  391.         }  
  392.     }  
  393.       
  394.     /** 
  395.      *  分层遍历二叉树(递归) 
  396.      *  很少有人会用递归去做level traversal 
  397.      *  基本思想是用一个大的ArrayList,里面包含了每一层的ArrayList。 
  398.      *  大的ArrayList的size和level有关系 
  399.      *   
  400.      *  这是我目前见到的最好的递归解法! 
  401.      *  http://discuss.leetcode.com/questions/49/binary-tree-level-order-traversal#answer-container-2543 
  402.      */  
  403.     public static void levelTraversalRec(TreeNode root) {  
  404.         ArrayList<ArrayList<Integer>> ret = new ArrayList<ArrayList<Integer>>();  
  405.         dfs(root, 0, ret);  
  406.         System.out.println(ret);  
  407.     }  
  408.       
  409.     private static void dfs(TreeNode root, int level, ArrayList<ArrayList<Integer>> ret){  
  410.         if(root == null){  
  411.             return;  
  412.         }  
  413.           
  414.         // 添加一个新的ArrayList表示新的一层  
  415.         if(level >= ret.size()){  
  416.             ret.add(new ArrayList<Integer>());  
  417.         }  
  418.           
  419.         ret.get(level).add(root.val);   // 把节点添加到表示那一层的ArrayList里  
  420.         dfs(root.left, level+1, ret);       // 递归处理下一层的左子树和右子树  
  421.         dfs(root.right, level+1, ret);  
  422.     }  
  423.       
  424.   
  425.     /** 
  426.      * 将二叉查找树变为有序的双向链表 要求不能创建新节点,只调整指针。  
  427.      * 递归解法: 
  428.      * 参考了http://stackoverflow.com/questions/11511898/converting-a-binary-search-tree-to-doubly-linked-list#answer-11530016 
  429.      * 感觉是最清晰的递归解法,但要注意递归完,root会在链表的中间位置,因此要手动 
  430.      * 把root移到链表头或链表尾 
  431.      */  
  432.     public static TreeNode convertBST2DLLRec(TreeNode root) {  
  433.         root = convertBST2DLLSubRec(root);  
  434.           
  435.         // root会在链表的中间位置,因此要手动把root移到链表头  
  436.         while(root.left != null){  
  437.             root = root.left;  
  438.         }  
  439.         return root;  
  440.     }  
  441.       
  442.     /** 
  443.      *  递归转换BST为双向链表(DLL) 
  444.      */  
  445.     public static TreeNode convertBST2DLLSubRec(TreeNode root){  
  446.         if(root==null || (root.left==null && root.right==null)){  
  447.             return root;  
  448.         }  
  449.           
  450.         TreeNode tmp = null;  
  451.         if(root.left != null){          // 处理左子树  
  452.             tmp = convertBST2DLLSubRec(root.left);  
  453.             while(tmp.right != null){   // 寻找最右节点  
  454.                 tmp = tmp.right;  
  455.             }  
  456.             tmp.right = root;       // 把左子树处理后结果和root连接  
  457.             root.left = tmp;  
  458.         }  
  459.         if(root.right != null){     // 处理右子树  
  460.             tmp = convertBST2DLLSubRec(root.right);  
  461.             while(tmp.left != null){    // 寻找最左节点  
  462.                 tmp = tmp.left;  
  463.             }  
  464.             tmp.left = root;        // 把右子树处理后结果和root连接  
  465.             root.right = tmp;  
  466.         }  
  467.         return root;  
  468.     }  
  469.       
  470.     /** 
  471.      * 将二叉查找树变为有序的双向链表 迭代解法 
  472. //   * 类似inorder traversal的做法 
  473.      */  
  474.     public static TreeNode convertBST2DLL(TreeNode root) {  
  475.         if(root == null){  
  476.             return null;  
  477.         }  
  478.         Stack<TreeNode> stack = new Stack<TreeNode>();  
  479.         TreeNode cur = root;        // 指向当前处理节点  
  480.         TreeNode old = null;            // 指向前一个处理的节点  
  481.         TreeNode head = null;       // 链表头  
  482.           
  483.         whiletrue ){  
  484.             while(cur != null){     // 先添加一个非空节点所有的左孩子到栈  
  485.                 stack.push(cur);  
  486.                 cur = cur.left;  
  487.             }  
  488.               
  489.             if(stack.isEmpty()){  
  490.                 break;  
  491.             }  
  492.                   
  493.             // 因为此时已经没有左孩子了,所以输出栈顶元素  
  494.             cur = stack.pop();  
  495.             if(old != null){  
  496.                 old.right = cur;  
  497.             }  
  498.             if(head == null){       // /第一个节点为双向链表头节点  
  499.                 head = cur;  
  500.             }  
  501.               
  502.             old = cur;          // 更新old  
  503.             cur = cur.right;    // 准备处理右子树  
  504.         }  
  505.           
  506.         return head;  
  507.     }  
  508.   
  509.     /** 
  510.      * 求二叉树第K层的节点个数   递归解法:  
  511.      * (1)如果二叉树为空或者k<1返回0 
  512.      * (2)如果二叉树不为空并且k==1,返回1 
  513.      * (3)如果二叉树不为空且k>1,返回root左子树中k-1层的节点个数与root右子树k-1层节点个数之和 
  514.      *  
  515.      * 求以root为根的k层节点数目 等价于 求以root左孩子为根的k-1层(因为少了root那一层)节点数目 加上 
  516.      * 以root右孩子为根的k-1层(因为少了root那一层)节点数目 
  517.      *  
  518.      * 所以遇到树,先把它拆成左子树和右子树,把问题降解 
  519.      *  
  520.      */  
  521.     public static int getNodeNumKthLevelRec(TreeNode root, int k) {  
  522.         if (root == null || k < 1) {  
  523.             return 0;  
  524.         }  
  525.   
  526.         if (k == 1) {  
  527.             return 1;  
  528.         }  
  529.         int numLeft = getNodeNumKthLevelRec(root.left, k - 1);      // 求root左子树的k-1层节点数  
  530.         int numRight = getNodeNumKthLevelRec(root.right, k - 1);    // 求root右子树的k-1层节点数  
  531.         return numLeft + numRight;  
  532.     }  
  533.       
  534.     /** 
  535.      *  求二叉树第K层的节点个数   迭代解法:  
  536.      *  同getDepth的迭代解法 
  537.      */  
  538.     public static int getNodeNumKthLevel(TreeNode root, int k){  
  539.         if(root == null){  
  540.             return 0;  
  541.         }  
  542.         Queue<TreeNode> queue = new LinkedList<TreeNode>();  
  543.         queue.add(root);  
  544.           
  545.         int i = 1;  
  546.         int currentLevelNodes = 1;      // 当前Level,node的数量  
  547.         int nextLevelNodes = 0;         // 下一层Level,node的数量  
  548.           
  549.         while( !queue.isEmpty() && i<k){  
  550.             TreeNode cur = queue.remove();      // 从队头位置移除  
  551.             currentLevelNodes--;            // 减少当前Level node的数量  
  552.             if(cur.left != null){               // 如果有左孩子,加到队尾  
  553.                 queue.add(cur.left);  
  554.                 nextLevelNodes++;           // 并增加下一层Level node的数量  
  555.             }  
  556.             if(cur.right != null){          // 如果有右孩子,加到队尾  
  557.                 queue.add(cur.right);  
  558.                 nextLevelNodes++;  
  559.             }  
  560.               
  561.             if(currentLevelNodes == 0){ // 说明已经遍历完当前层的所有节点  
  562.                 currentLevelNodes = nextLevelNodes;     // 初始化下一层的遍历  
  563.                 nextLevelNodes = 0;  
  564.                 i++;            // 进入到下一层  
  565.             }  
  566.         }  
  567.           
  568.         return currentLevelNodes;  
  569.     }  
  570.   
  571.     /** 
  572.      * 求二叉树中叶子节点的个数(递归) 
  573.      */  
  574.     public static int getNodeNumLeafRec(TreeNode root) {  
  575.         // 当root不存在,返回空  
  576.         if (root == null) {  
  577.             return 0;  
  578.         }  
  579.   
  580.         // 当为叶子节点时返回1  
  581.         if (root.left == null && root.right == null) {  
  582.             return 1;  
  583.         }  
  584.   
  585.         // 把一个树拆成左子树和右子树之和,原理同上一题  
  586.         return getNodeNumLeafRec(root.left) + getNodeNumLeafRec(root.right);  
  587.     }  
  588.       
  589.     /** 
  590.      *  求二叉树中叶子节点的个数(迭代) 
  591.      *  还是基于Level order traversal 
  592.      */  
  593.     public static int getNodeNumLeaf(TreeNode root) {  
  594.         if(root == null){  
  595.             return 0;  
  596.         }  
  597.         Queue<TreeNode> queue = new LinkedList<TreeNode>();  
  598.         queue.add(root);  
  599.           
  600.         int leafNodes = 0;              // 记录上一个Level,node的数量  
  601.           
  602.         while( !queue.isEmpty() ){  
  603.             TreeNode cur = queue.remove();      // 从队头位置移除  
  604.             if(cur.left != null){               // 如果有左孩子,加到队尾  
  605.                 queue.add(cur.left);  
  606.             }  
  607.             if(cur.right != null){              // 如果有右孩子,加到队尾  
  608.                 queue.add(cur.right);  
  609.             }  
  610.             if(cur.left==null && cur.right==null){          // 叶子节点  
  611.                 leafNodes++;  
  612.             }  
  613.         }  
  614.           
  615.         return leafNodes;  
  616.     }  
  617.   
  618.     /** 
  619.      * 判断两棵二叉树是否相同的树。 
  620.      * 递归解法:  
  621.      * (1)如果两棵二叉树都为空,返回真 
  622.      * (2)如果两棵二叉树一棵为空,另一棵不为空,返回假  
  623.      * (3)如果两棵二叉树都不为空,如果对应的左子树和右子树都同构返回真,其他返回假 
  624.      */  
  625.     public static boolean isSameRec(TreeNode r1, TreeNode r2) {  
  626.         // 如果两棵二叉树都为空,返回真  
  627.         if (r1 == null && r2 == null) {  
  628.             return true;  
  629.         }  
  630.         // 如果两棵二叉树一棵为空,另一棵不为空,返回假  
  631.         else if (r1 == null || r2 == null) {  
  632.             return false;  
  633.         }  
  634.   
  635.         if(r1.val != r2.val){  
  636.             return false;  
  637.         }  
  638.         boolean leftRes = isSameRec(r1.left, r2.left);      // 比较对应左子树  
  639.         boolean rightRes = isSameRec(r1.right, r2.right); // 比较对应右子树  
  640.         return leftRes && rightRes;  
  641.     }  
  642.       
  643.     /** 
  644.      * 判断两棵二叉树是否相同的树(迭代) 
  645.      * 遍历一遍即可,这里用preorder 
  646.      */  
  647.     public static boolean isSame(TreeNode r1, TreeNode r2) {  
  648.         // 如果两个树都是空树,则返回true  
  649.         if(r1==null && r2==null){  
  650.             return true;  
  651.         }  
  652.           
  653.         // 如果有一棵树是空树,另一颗不是,则返回false  
  654.         if(r1==null || r2==null){  
  655.             return false;  
  656.         }  
  657.           
  658.         Stack<TreeNode> s1 = new Stack<TreeNode>();  
  659.         Stack<TreeNode> s2 = new Stack<TreeNode>();  
  660.           
  661.         s1.push(r1);  
  662.         s2.push(r2);  
  663.           
  664.         while(!s1.isEmpty() && !s2.isEmpty()){  
  665.             TreeNode n1 = s1.pop();  
  666.             TreeNode n2 = s2.pop();  
  667.             if(n1==null && n2==null){  
  668.                 continue;  
  669.             }else if(n1!=null && n2!=null && n1.val==n2.val){  
  670.                 s1.push(n1.right);  
  671.                 s1.push(n1.left);  
  672.                 s2.push(n2.right);  
  673.                 s2.push(n2.left);  
  674.             }else{  
  675.                 return false;  
  676.             }  
  677.         }  
  678.         return true;  
  679.     }  
  680.   
  681.     /** 
  682.      * 判断二叉树是不是平衡二叉树 递归解法:  
  683.      * (1)如果二叉树为空,返回真 
  684.      * (2)如果二叉树不为空,如果左子树和右子树都是AVL树并且左子树和右子树高度相差不大于1,返回真,其他返回假 
  685.      */  
  686.     public static boolean isAVLRec(TreeNode root) {  
  687.         if(root == null){           // 如果二叉树为空,返回真  
  688.             return true;  
  689.         }  
  690.           
  691.         // 如果左子树和右子树高度相差大于1,则非平衡二叉树, getDepthRec()是前面实现过的求树高度的方法  
  692.         if(Math.abs(getDepthRec(root.left) - getDepthRec(root.right)) > 1){  
  693.             return false;  
  694.         }  
  695.           
  696.         // 递归判断左子树和右子树是否为平衡二叉树  
  697.         return isAVLRec(root.left) && isAVLRec(root.right);  
  698.     }  
  699.       
  700.   
  701.     /** 
  702.      * 求二叉树的镜像 递归解法:  
  703.      * (1)如果二叉树为空,返回空 
  704.      * (2)如果二叉树不为空,求左子树和右子树的镜像,然后交换左子树和右子树 
  705.      */  
  706.     // 1. 破坏原来的树,把原来的树改成其镜像  
  707.     public static TreeNode mirrorRec(TreeNode root) {  
  708.         if (root == null) {  
  709.             return null;  
  710.         }  
  711.   
  712.         TreeNode left = mirrorRec(root.left);  
  713.         TreeNode right = mirrorRec(root.right);  
  714.   
  715.         root.left = right;  
  716.         root.right = left;  
  717.         return root;  
  718.     }  
  719.       
  720.     // 2. 不能破坏原来的树,返回一个新的镜像树  
  721.     public static TreeNode mirrorCopyRec(TreeNode root){  
  722.         if(root == null){  
  723.             return null;  
  724.         }  
  725.           
  726.         TreeNode newNode = new TreeNode(root.val);  
  727.         newNode.left = mirrorCopyRec(root.right);  
  728.         newNode.right = mirrorCopyRec(root.left);  
  729.           
  730.         return newNode;  
  731.     }  
  732.       
  733.     // 3. 判断两个树是否互相镜像  
  734.     public static boolean isMirrorRec(TreeNode r1, TreeNode r2){  
  735.         // 如果两个树都是空树,则返回true  
  736.         if(r1==null && r2==null){  
  737.             return true;  
  738.         }  
  739.           
  740.         // 如果有一棵树是空树,另一颗不是,则返回false  
  741.         if(r1==null || r2==null){  
  742.             return false;  
  743.         }  
  744.           
  745.         // 如果两个树都非空树,则先比较根节点  
  746.         if(r1.val != r2.val){  
  747.             return false;  
  748.         }  
  749.           
  750.         // 递归比较r1的左子树的镜像是不是r2右子树 和   
  751.         // r1的右子树的镜像是不是r2左子树  
  752.         return isMirrorRec(r1.left, r2.right) && isMirrorRec(r1.right, r2.left);  
  753.     }  
  754.       
  755.     // 1. 破坏原来的树,把原来的树改成其镜像  
  756.     public static void mirror(TreeNode root) {  
  757.         if(root == null){  
  758.             return;  
  759.         }  
  760.           
  761.         Stack<TreeNode> stack = new Stack<TreeNode>();  
  762.         stack.push(root);  
  763.         while( !stack.isEmpty() ){  
  764.             TreeNode cur = stack.pop();  
  765.               
  766.             // 交换左右孩子  
  767.             TreeNode tmp = cur.right;  
  768.             cur.right = cur.left;  
  769.             cur.left = tmp;  
  770.               
  771.             if(cur.right != null){  
  772.                 stack.push(cur.right);  
  773.             }  
  774.             if(cur.left != null){  
  775.                 stack.push(cur.left);  
  776.             }  
  777.         }  
  778.     }  
  779.       
  780.     // 2. 不能破坏原来的树,返回一个新的镜像树  
  781.     public static TreeNode mirrorCopy(TreeNode root){  
  782.         if(root == null){  
  783.             return null;  
  784.         }  
  785.           
  786.         Stack<TreeNode> stack = new Stack<TreeNode>();  
  787.         Stack<TreeNode> newStack = new Stack<TreeNode>();  
  788.         stack.push(root);  
  789.         TreeNode newRoot = new TreeNode(root.val);  
  790.         newStack.push(newRoot);  
  791.           
  792.         while( !stack.isEmpty() ){  
  793.             TreeNode cur = stack.pop();  
  794.             TreeNode newCur = newStack.pop();  
  795.               
  796.             if(cur.right != null){  
  797.                 stack.push(cur.right);  
  798.                 newCur.left = new TreeNode(cur.right.val);  
  799.                 newStack.push(newCur.left);  
  800.             }  
  801.             if(cur.left != null){  
  802.                 stack.push(cur.left);  
  803.                 newCur.right = new TreeNode(cur.left.val);  
  804.                 newStack.push(newCur.right);  
  805.             }  
  806.         }  
  807.           
  808.         return newRoot;  
  809.     }  
  810.       
  811.   
  812.     /** 
  813.      * 求二叉树中两个节点的最低公共祖先节点  
  814.      * 递归解法:  
  815.      * (1)如果两个节点分别在根节点的左子树和右子树,则返回根节点 
  816.      * (2)如果两个节点都在左子树,则递归处理左子树;如果两个节点都在右子树,则递归处理右子树 
  817.      */  
  818.     public static TreeNode getLastCommonParentRec(TreeNode root, TreeNode n1, TreeNode n2) {  
  819.         if (findNodeRec(root.left, n1)) {               // 如果n1在树的左子树  
  820.             if (findNodeRec(root.right, n2)) {      // 如果n2在树的右子树  
  821.                 return root;                                // 返回根节点  
  822.             } else {            // 如果n2也在树的左子树  
  823.                 return getLastCommonParentRec(root.left, n1, n2); // 递归处理  
  824.             }  
  825.         } else {                // 如果n1在树的右子树  
  826.             if (findNodeRec(root.left, n2)) {           // 如果n2在左子树  
  827.                 return root;  
  828.             } else {                 // 如果n2在右子树  
  829.                 return getLastCommonParentRec(root.right, n1, n2); // 递归处理  
  830.             }  
  831.         }  
  832.     }  
  833.   
  834.     // 帮助方法,递归判断一个点是否在树里  
  835.     private static boolean findNodeRec(TreeNode root, TreeNode node) {  
  836.         if (root == null || node == null) {  
  837.             return false;  
  838.         }  
  839.         if (root == node) {  
  840.             return true;  
  841.         }  
  842.   
  843.         // 先尝试在左子树中查找  
  844.         boolean found = findNodeRec(root.left, node);  
  845.         if (!found) {       // 如果查找不到,再在右子树中查找  
  846.             found = findNodeRec(root.right, node);  
  847.         }  
  848.         return found;  
  849.     }  
  850.       
  851.     // 求二叉树中两个节点的最低公共祖先节点 (更加简洁版的递归)  
  852.     public static TreeNode getLastCommonParentRec2(TreeNode root, TreeNode n1, TreeNode n2) {  
  853.         if(root == null){  
  854.             return null;  
  855.         }  
  856.           
  857.         // 如果有一个match,则说明当前node就是要找的最低公共祖先  
  858.         if(root.equals(n1) || root.equals(n2)){  
  859.             return root;  
  860.         }  
  861.         TreeNode commonInLeft = getLastCommonParentRec2(root.left, n1, n2);  
  862.         TreeNode commonInRight = getLastCommonParentRec2(root.right, n1, n2);  
  863.           
  864.         // 如果一个左子树找到,一个在右子树找到,则说明root是唯一可能的最低公共祖先  
  865.         if(commonInLeft!=null && commonInRight!=null){  
  866.             return root;  
  867.         }  
  868.           
  869.         // 其他情况是要不然在左子树要不然在右子树  
  870.         if(commonInLeft != null){  
  871.             return commonInLeft;  
  872.         }  
  873.           
  874.         return commonInRight;  
  875.     }  
  876.   
  877.     /** 
  878.      * 非递归解法:  
  879.      * 先求从根节点到两个节点的路径,然后再比较对应路径的节点就行,最后一个相同的节点也就是他们在二叉树中的最低公共祖先节点 
  880.      */  
  881.     public static TreeNode getLastCommonParent(TreeNode root, TreeNode n1, TreeNode n2) {  
  882.         if (root == null || n1 == null || n2 == null) {  
  883.             return null;  
  884.         }  
  885.   
  886.         ArrayList<TreeNode> p1 = new ArrayList<TreeNode>();  
  887.         boolean res1 = getNodePath(root, n1, p1);  
  888.         ArrayList<TreeNode> p2 = new ArrayList<TreeNode>();  
  889.         boolean res2 = getNodePath(root, n2, p2);  
  890.   
  891.         if (!res1 || !res2) {  
  892.             return null;  
  893.         }  
  894.   
  895.         TreeNode last = null;  
  896.         Iterator<TreeNode> iter1 = p1.iterator();  
  897.         Iterator<TreeNode> iter2 = p2.iterator();  
  898.   
  899.         while (iter1.hasNext() && iter2.hasNext()) {  
  900.             TreeNode tmp1 = iter1.next();  
  901.             TreeNode tmp2 = iter2.next();  
  902.             if (tmp1 == tmp2) {  
  903.                 last = tmp1;  
  904.             } else { // 直到遇到非公共节点  
  905.                 break;  
  906.             }  
  907.         }  
  908.         return last;  
  909.     }  
  910.   
  911.     // 把从根节点到node路径上所有的点都添加到path中  
  912.     private static boolean getNodePath(TreeNode root, TreeNode node, ArrayList<TreeNode> path) {  
  913.         if (root == null) {  
  914.             return false;  
  915.         }  
  916.           
  917.         path.add(root);     // 把这个节点加到路径中  
  918.         if (root == node) {  
  919.             return true;  
  920.         }  
  921.   
  922.         boolean found = false;  
  923.         found = getNodePath(root.left, node, path); // 先在左子树中找  
  924.           
  925.         if (!found) {               // 如果没找到,再在右子树找  
  926.             found = getNodePath(root.right, node, path);  
  927.         }  
  928.         if (!found) {               // 如果实在没找到证明这个节点不在路径中,说明刚才添加进去的不是路径上的节点,删掉!  
  929.             path.remove(root);    
  930.         }  
  931.   
  932.         return found;  
  933.     }  
  934.   
  935.     /** 
  936.      * 求二叉树中节点的最大距离 即二叉树中相距最远的两个节点之间的距离。 (distance / diameter) 
  937.      * 递归解法:  
  938.      * (1)如果二叉树为空,返回0,同时记录左子树和右子树的深度,都为0 
  939.      * (2)如果二叉树不为空,最大距离要么是左子树中的最大距离,要么是右子树中的最大距离, 
  940.      * 要么是左子树节点中到根节点的最大距离+右子树节点中到根节点的最大距离, 
  941.      * 同时记录左子树和右子树节点中到根节点的最大距离。 
  942.      *  
  943.      * http://www.cnblogs.com/miloyip/archive/2010/02/25/1673114.html 
  944.      *  
  945.      * 计算一个二叉树的最大距离有两个情况: 
  946.  
  947.         情况A: 路径经过左子树的最深节点,通过根节点,再到右子树的最深节点。 
  948.         情况B: 路径不穿过根节点,而是左子树或右子树的最大距离路径,取其大者。 
  949.         只需要计算这两个情况的路径距离,并取其大者,就是该二叉树的最大距离 
  950.      */  
  951.     public static Result getMaxDistanceRec(TreeNode root){  
  952.         if(root == null){  
  953.             Result empty = new Result(0, -1);       // 目的是让调用方 +1 后,把当前的不存在的 (NULL) 子树当成最大深度为 0  
  954.             return empty;  
  955.         }  
  956.           
  957.         // 计算出左右子树分别最大距离  
  958.         Result lmd = getMaxDistanceRec(root.left);  
  959.         Result rmd = getMaxDistanceRec(root.right);  
  960.           
  961.         Result res = new Result();  
  962.         res.maxDepth = Math.max(lmd.maxDepth, rmd.maxDepth) + 1;        // 当前最大深度  
  963.         // 取情况A和情况B中较大值  
  964.         res.maxDistance = Math.max( lmd.maxDepth+rmd.maxDepth, Math.max(lmd.maxDistance, rmd.maxDistance) );  
  965.         return res;  
  966.     }  
  967.       
  968.     private static class Result{  
  969.         int maxDistance;  
  970.         int maxDepth;  
  971.         public Result() {  
  972.         }  
  973.   
  974.         public Result(int maxDistance, int maxDepth) {  
  975.             this.maxDistance = maxDistance;  
  976.             this.maxDepth = maxDepth;  
  977.         }  
  978.     }  
  979.       
  980.     /** 
  981.      * 13. 由前序遍历序列和中序遍历序列重建二叉树(递归) 
  982.      * 感觉这篇是讲的最为清晰的: 
  983.      * http://crackinterviewtoday.wordpress.com/2010/03/15/rebuild-a-binary-tree-from-inorder-and-preorder-traversals/ 
  984.      * 文中还提到一种避免开额外空间的方法,等下次补上 
  985.      */  
  986.     public static TreeNode rebuildBinaryTreeRec(List<Integer> preOrder, List<Integer> inOrder){  
  987.         TreeNode root = null;  
  988.         List<Integer> leftPreOrder;  
  989.         List<Integer> rightPreOrder;  
  990.         List<Integer> leftInorder;  
  991.         List<Integer> rightInorder;  
  992.         int inorderPos;  
  993.         int preorderPos;  
  994.    
  995.         if ((preOrder.size() != 0) && (inOrder.size() != 0))  
  996.         {  
  997.             // 把preorder的第一个元素作为root  
  998.             root = new TreeNode(preOrder.get(0));  
  999.    
  1000.             //  Based upon the current node data seperate the traversals into leftPreorder, rightPreorder,  
  1001.             //  leftInorder, rightInorder lists  
  1002.             // 因为知道root节点了,所以根据root节点位置,把preorder,inorder分别划分为 root左侧 和 右侧 的两个子区间  
  1003.             inorderPos = inOrder.indexOf(preOrder.get(0));      // inorder序列的分割点  
  1004.             leftInorder = inOrder.subList(0, inorderPos);  
  1005.             rightInorder = inOrder.subList(inorderPos + 1, inOrder.size());  
  1006.    
  1007.             preorderPos = leftInorder.size();                           // preorder序列的分割点  
  1008.             leftPreOrder = preOrder.subList(1, preorderPos + 1);  
  1009.             rightPreOrder = preOrder.subList(preorderPos + 1, preOrder.size());  
  1010.    
  1011.             root.left = rebuildBinaryTreeRec(leftPreOrder, leftInorder);        // root的左子树就是preorder和inorder的左侧区间而形成的树  
  1012.             root.right = rebuildBinaryTreeRec(rightPreOrder, rightInorder); // root的右子树就是preorder和inorder的右侧区间而形成的树  
  1013.         }  
  1014.    
  1015.         return root;  
  1016.     }  
  1017.       
  1018.     /** 
  1019.         14.  判断二叉树是不是完全二叉树(迭代) 
  1020.         若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数, 
  1021.         第 h 层所有的结点都连续集中在最左边,这就是完全二叉树。 
  1022.         有如下算法,按层次(从上到下,从左到右)遍历二叉树,当遇到一个节点的左子树为空时, 
  1023.         则该节点右子树必须为空,且后面遍历的节点左右子树都必须为空,否则不是完全二叉树。 
  1024.      */  
  1025.     public static boolean isCompleteBinaryTree(TreeNode root){  
  1026.         if(root == null){  
  1027.             return false;  
  1028.         }  
  1029.           
  1030.         Queue<TreeNode> queue = new LinkedList<TreeNode>();  
  1031.         queue.add(root);  
  1032.         boolean mustHaveNoChild = false;  
  1033.         boolean result = true;  
  1034.           
  1035.         while( !queue.isEmpty() ){  
  1036.             TreeNode cur = queue.remove();  
  1037.             if(mustHaveNoChild){    // 已经出现了有空子树的节点了,后面出现的必须为叶节点(左右子树都为空)    
  1038.                 if(cur.left!=null || cur.right!=null){  
  1039.                     result = false;  
  1040.                     break;  
  1041.                 }  
  1042.             } else {  
  1043.                 if(cur.left!=null && cur.right!=null){      // 如果左子树和右子树都非空,则继续遍历  
  1044.                     queue.add(cur.left);  
  1045.                     queue.add(cur.right);  
  1046.                 }else if(cur.left!=null && cur.right==null){    // 如果左子树非空但右子树为空,说明已经出现空节点,之后必须都为空子树  
  1047.                     mustHaveNoChild = true;  
  1048.                     queue.add(cur.left);  
  1049.                 }else if(cur.left==null && cur.right!=null){    // 如果左子树为空但右子树非空,说明这棵树已经不是完全二叉完全树!  
  1050.                     result = false;  
  1051.                     break;  
  1052.                 }else{          // 如果左右子树都为空,则后面的必须也都为空子树  
  1053.                     mustHaveNoChild = true;  
  1054.                 }  
  1055.             }  
  1056.         }  
  1057.         return result;  
  1058.     }  
  1059.       
  1060.     /** 
  1061.      * 14.  判断二叉树是不是完全二叉树(递归) 
  1062.      * http://stackoverflow.com/questions/1442674/how-to-determine-whether-a-binary-tree-is-complete 
  1063.      *  
  1064.      */  
  1065.     public static boolean isCompleteBinaryTreeRec(TreeNode root){  
  1066. //      Pair notComplete = new Pair(-1, false);  
  1067. //      return !isCompleteBinaryTreeSubRec(root).equalsTo(notComplete);  
  1068.         return isCompleteBinaryTreeSubRec(root).height != -1;  
  1069.     }  
  1070.       
  1071.     // 递归判断是否满树(完美)  
  1072.     public static boolean isPerfectBinaryTreeRec(TreeNode root){  
  1073.         return isCompleteBinaryTreeSubRec(root).isFull;  
  1074.     }  
  1075.       
  1076.     // 递归,要创建一个Pair class来保存树的高度和是否已满的信息  
  1077.     public static Pair isCompleteBinaryTreeSubRec(TreeNode root){  
  1078.         if(root == null){  
  1079.             return new Pair(0true);  
  1080.         }  
  1081.           
  1082.         Pair left = isCompleteBinaryTreeSubRec(root.left);  
  1083.         Pair right = isCompleteBinaryTreeSubRec(root.right);  
  1084.           
  1085.         // 左树满节点,而且左右树相同高度,则是唯一可能形成满树(若右树也是满节点)的情况  
  1086.         if(left.isFull && left.height==right.height){  
  1087.             return new Pair(1+left.height, right.isFull);  
  1088.         }  
  1089.           
  1090.         // 左树非满,但右树是满节点,且左树高度比右树高一  
  1091.         // 注意到如果其左树为非完全树,则它的高度已经被设置成-1,  
  1092.         // 因此不可能满足第二个条件!  
  1093.         if(right.isFull && left.height==right.height+1){  
  1094.             return new Pair(1+left.height, false);  
  1095.         }  
  1096.           
  1097.         // 其他情况都是非完全树,直接设置高度为-1  
  1098.         return new Pair(-1false);  
  1099.     }  
  1100.       
  1101.     private static class Pair{  
  1102.         int height;             // 树的高度  
  1103.         boolean isFull;     // 是否是个满树  
  1104.   
  1105.         public Pair(int height, boolean isFull) {  
  1106.             this.height = height;  
  1107.             this.isFull = isFull;  
  1108.         }  
  1109.   
  1110.         public boolean equalsTo(Pair obj){  
  1111.             return this.height==obj.height && this.isFull==obj.isFull;  
  1112.         }  
  1113.     }  
  1114.       
  1115. }  
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值