zoukankan      html  css  js  c++  java
  • java——二叉树面试题

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

    意的一个二叉树,都可以补成一个满二叉树。这样中间就会有很多空洞。在广度优先遍历的时候,如果是满二叉树,或者完全二叉树,这些空洞是在广度优先的遍历的末尾,所以,但我们遍历到空洞的时候,整个二叉树就已经遍历完成了。而如果,是非完全二叉树,

    我们遍历到空洞的时候,就会发现,空洞后面还有没有遍历到的值。这样,只要根据是否遍历到空洞,整个树的遍历是否结束来判断是否是完全的二叉树。

     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
  • 相关阅读:
    电赛小结
    markdown小结
    一元运算符重载
    二维数组作为函数参数传递剖析(转载)
    C语言内存(转载)
    Effective C++ chapter1:Accustiming Yourself to C++
    C++ 模板
    const
    命令行参数
    AStar算法
  • 原文地址:https://www.cnblogs.com/zxqstrong/p/5312963.html
Copyright © 2011-2022 走看看