二叉树大全

package BinaryTreeSummary;  
   
import java.util.ArrayList;  
import java.util.Iterator;  
import java.util.LinkedList;  
import java.util.List;  
import java.util.Queue;  
import java.util.Stack;  
   
/** 
 * http://blog.csdn.net/luckyxiaoqiang/article/details/7518888  轻松搞定面试中的二叉树题目 
 * http://www.cnblogs.com/Jax/archive/2009/12/28/1633691.html  算法大全(3) 二叉树 
 *  
 * TODO: 必定要能熟练地写出全部问题的递归和非递归作法! 
 * 
 * 1. 求二叉树中的节点个数: 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 
 *  
 */ 
public class Demo {  
   
    /* 
                 1  
                / \  
               2   3  
              / \   \  
             4  5   6  
     */ 
    public static void main(String[] args) {  
        TreeNode r1 = new TreeNode(1);  
        TreeNode r2 = new TreeNode(2);  
        TreeNode r3 = new TreeNode(3);  
        TreeNode r4 = new TreeNode(4);  
        TreeNode r5 = new TreeNode(5);  
        TreeNode r6 = new TreeNode(6);  
           
        r1.left = r2;  
        r1.right = r3;  
        r2.left = r4;  
        r2.right = r5;  
        r3.right = r6;  
           
//      System.out.println(getNodeNumRec(r1));  
//      System.out.println(getNodeNum(r1));  
//      System.out.println(getDepthRec(r1));  
//      System.out.println(getDepth(r1));  
           
//      preorderTraversalRec(r1);  
//      System.out.println();  
//      preorderTraversal(r1);  
//      System.out.println();  
//      inorderTraversalRec(r1);  
//      System.out.println();  
//      inorderTraversal(r1);  
//      System.out.println();  
//      postorderTraversalRec(r1);  
//      System.out.println();  
//      postorderTraversal(r1);  
//      System.out.println();  
//      levelTraversal(r1);  
//      System.out.println();  
//      levelTraversalRec(r1);  
//      System.out.println();  
           
//      TreeNode tmp = convertBSTRec(r1);  
//      while(true){  
//          if(tmp == null){  
//              break;  
//          }  
//          System.out.print(tmp.val + " ");  
//          if(tmp.right == null){  
//              break;  
//          }  
//          tmp = tmp.right;  
//      }  
//      System.out.println();  
//      while(true){  
//          if(tmp == null){  
//              break;  
//          }  
//          System.out.print(tmp.val + " ");  
//          if(tmp.left == null){  
//              break;  
//          }  
//          tmp = tmp.left;  
//      }  
           
           
//      TreeNode tmp = convertBST2DLL(r1);  
//      while(true){  
//          if(tmp == null){  
//              break;  
//          }  
//          System.out.print(tmp.val + " ");  
//          if(tmp.right == null){  
//              break;  
//          }  
//          tmp = tmp.right;  
//      }  
           
//      System.out.println(getNodeNumKthLevelRec(r1, 2));  
//      System.out.println(getNodeNumKthLevel(r1, 2));  
           
//      System.out.println(getNodeNumLeafRec(r1));  
//      System.out.println(getNodeNumLeaf(r1));  
           
//      System.out.println(isSame(r1, r1));  
//      inorderTraversal(r1);  
//      System.out.println();  
//      mirror(r1);  
//      TreeNode mirrorRoot = mirrorCopy(r1);  
//      inorderTraversal(mirrorRoot);  
           
        System.out.println(isCompleteBinaryTree(r1));  
        System.out.println(isCompleteBinaryTreeRec(r1));  
           
    }  
   
    private static class TreeNode {  
        int val;  
        TreeNode left;  
        TreeNode right;  
   
        public TreeNode(int val) {  
            this.val = val;  
        }  
    }  
   
    /** 
     * 求二叉树中的节点个数递归解法: O(n) 
     * (1)若是二叉树为空,节点个数为0  
     * (2)若是二叉树不为空,二叉树节点个数 = 左子树节点个数 + 
     *            右子树节点个数 + 1 
     */ 
    public static int getNodeNumRec(TreeNode root) {  
        if (root == null) {  
            return 0;  
        } else {  
            return getNodeNumRec(root.left) + getNodeNumRec(root.right) + 1;  
        }  
    }  
       
    /** 
     *  求二叉树中的节点个数迭代解法O(n):基本思想同LevelOrderTraversal, 
     *  即用一个Queue,在Java里面能够用LinkedList来模拟  
     */ 
    public static int getNodeNum(TreeNode root) {  
        if(root == null){  
            return 0;  
        }  
        int count = 1;  
        Queue<TreeNode> queue = new LinkedList<TreeNode>();  
        queue.add(root);  
           
        while(!queue.isEmpty()){  
            TreeNode cur = queue.remove();      // 从队头位置移除  
            if(cur.left != null){           // 若是有左孩子,加到队尾  
                queue.add(cur.left);  
                count++;  
            }  
            if(cur.right != null){      // 若是有右孩子,加到队尾  
                queue.add(cur.right);  
                count++;  
            }  
        }  
           
        return count;  
    }  
   
    /** 
     * 求二叉树的深度(高度) 递归解法: O(n) 
     * (1)若是二叉树为空,二叉树的深度为0  
     * (2)若是二叉树不为空,二叉树的深度 = max(左子树深度, 右子树深度) + 1 
     */ 
    public static int getDepthRec(TreeNode root) {  
        if (root == null) {  
            return 0;  
        }  
   
        int leftDepth = getDepthRec(root.left);  
        int rightDepth = getDepthRec(root.right);  
        return Math.max(leftDepth, rightDepth) + 1;  
    }  
       
    /** 
     * 求二叉树的深度(高度) 迭代解法: O(n) 
     * 基本思想同LevelOrderTraversal,仍是用一个Queue 
     */ 
    public static int getDepth(TreeNode root) {  
        if(root == null){  
            return 0;  
        }  
           
        int depth = 0;                          // 深度  
        int currentLevelNodes = 1;      // 当前Level,node的数量  
        int nextLevelNodes = 0;         // 下一层Level,node的数量  
           
        LinkedList<TreeNode> queue = new LinkedList<TreeNode>();  
        queue.add(root);  
           
        while( !queue.isEmpty() ){  
            TreeNode cur = queue.remove();      // 从队头位置移除  
            currentLevelNodes--;            // 减小当前Level node的数量  
            if(cur.left != null){               // 若是有左孩子,加到队尾  
                queue.add(cur.left);  
                nextLevelNodes++;           // 并增长下一层Level node的数量  
            }  
            if(cur.right != null){          // 若是有右孩子,加到队尾  
                queue.add(cur.right);  
                nextLevelNodes++;  
            }  
               
            if(currentLevelNodes == 0){ // 说明已经遍历完当前层的全部节点  
                depth++;                       // 增长高度  
                currentLevelNodes = nextLevelNodes;     // 初始化下一层的遍历  
                nextLevelNodes = 0;  
            }  
        }  
           
        return depth;  
    }  
       
       
   
    /** 
     * 前序遍历,中序遍历,后序遍历 前序遍历递归解法:  
     * (1)若是二叉树为空,空操做  
     * (2)若是二叉树不为空,访问根节点,前序遍历左子树,前序遍历右子树 
     */ 
    public static void preorderTraversalRec(TreeNode root) {  
        if (root == null) {  
            return;  
        }  
        System.out.print(root.val + " ");  
        preorderTraversalRec(root.left);  
        preorderTraversalRec(root.right);  
    }  
       
    /** 
     *  前序遍历迭代解法:用一个辅助stack,老是把右孩子放进栈 
     *  http://www.youtube.com/watch?v=uPTCbdHSFg4 
     */ 
    public static void preorderTraversal(TreeNode root) {  
        if(root == null){  
            return;  
        }  
           
        Stack<TreeNode> stack = new Stack<TreeNode>();      // 辅助stack  
        stack.push(root);  
           
        while( !stack.isEmpty() ){  
            TreeNode cur = stack.pop();     // 出栈栈顶元素  
            System.out.print(cur.val + " ");  
               
            // 关键点:要先压入右孩子,再压入左孩子,这样在出栈时会先打印左孩子再打印右孩子  
            if(cur.right != null){  
                stack.push(cur.right);  
            }  
            if(cur.left != null){  
                stack.push(cur.left);  
            }  
        }  
    }  
   
    /** 
     * 中序遍历递归解法  
     * (1)若是二叉树为空,空操做。  
     * (2)若是二叉树不为空,中序遍历左子树,访问根节点,中序遍历右子树 
     */ 
    public static void inorderTraversalRec(TreeNode root) {  
        if (root == null) {  
            return;  
        }  
        inorderTraversalRec(root.left);  
        System.out.print(root.val + " ");  
        inorderTraversalRec(root.right);  
    }  
       
    /** 
     * 中序遍历迭代解法 ,用栈先把根节点的全部左孩子都添加到栈内, 
     * 而后输出栈顶元素,再处理栈顶元素的右子树 
     * http://www.youtube.com/watch?v=50v1sJkjxoc 
     *  
     * 还有一种方法能不用递归和栈,基于线索二叉树的方法,较麻烦之后补上 
     * http://www.geeksforgeeks.org/inorder-tree-traversal-without-recursion-and-without-stack/ 
     */ 
    public static void inorderTraversal(TreeNode root){  
        if(root == null){  
            return;  
        }  
        Stack<TreeNode> stack = new Stack<TreeNode>();  
        TreeNode cur = root;  
           
        while( true ){  
            while(cur != null){     // 先添加一个非空节点全部的左孩子到栈  
                stack.push(cur);  
                cur = cur.left;  
            }  
               
            if(stack.isEmpty()){  
                break;  
            }  
                   
            // 由于此时已经没有左孩子了,因此输出栈顶元素  
            cur = stack.pop();  
            System.out.print(cur.val + " ");  
            cur = cur.right;    // 准备处理右子树  
        }  
    }  
   
    /** 
     * 后序遍历递归解法  
     * (1)若是二叉树为空,空操做  
     * (2)若是二叉树不为空,后序遍历左子树,后序遍历右子树,访问根节点 
     */ 
    public static void postorderTraversalRec(TreeNode root) {  
        if (root == null) {  
            return;  
        }  
        postorderTraversalRec(root.left);  
        postorderTraversalRec(root.right);  
        System.out.print(root.val + " ");  
    }  
       
    /** 
     *  后序遍历迭代解法 
     *  http://www.youtube.com/watch?v=hv-mJUs5mvU 
     *   
     */ 
    public static void postorderTraversal(TreeNode root) {  
        if (root == null) {  
            return;  
        }  
           
        Stack<TreeNode> s = new Stack<TreeNode>();      // 第一个stack用于添加node和它的左右孩子  
        Stack<TreeNode> output = new Stack<TreeNode>();// 第二个stack用于翻转第一个stack输出  
           
        s.push(root);  
        while( !s.isEmpty() ){      // 确保全部元素都被翻转转移到第二个stack  
            TreeNode cur = s.pop(); // 把栈顶元素添加到第二个stack  
            output.push(cur);         
               
            if(cur.left != null){       // 把栈顶元素的左孩子和右孩子分别添加入第一个stack  
                s.push(cur.left);  
            }  
            if(cur.right != null){  
                s.push(cur.right);  
            }  
        }  
           
        while( !output.isEmpty() ){ // 遍历输出第二个stack,即为后序遍历  
            System.out.print(output.pop().val + " ");  
        }  
    }  
   
    /** 
     * 分层遍历二叉树(按层次从上往下,从左往右)迭代 
     * 至关于广度优先搜索,使用队列实现。队列初始化,将根节点压入队列。当队列不为空,进行以下操做:弹出一个节点 
     * ,访问,若左子节点或右子节点不为空,将其压入队列 
     */ 
    public static void levelTraversal(TreeNode root) {  
        if (root == null) {  
            return;  
        }  
        LinkedList<TreeNode> queue = new LinkedList<TreeNode>();  
        queue.push(root);  
   
        while (!queue.isEmpty()) {  
            TreeNode cur = queue.removeFirst();  
            System.out.print(cur.val + " ");  
            if (cur.left != null) {  
                queue.add(cur.left);  
            }  
            if (cur.right != null) {  
                queue.add(cur.right);  
            }  
        }  
    }  
       
    /** 
     *  分层遍历二叉树(递归) 
     *  不多有人会用递归去作level traversal 
     *  基本思想是用一个大的ArrayList,里面包含了每一层的ArrayList。 
     *  大的ArrayList的size和level有关系 
     *   
     *  这是我目前见到的最好的递归解法! 
     *  http://discuss.leetcode.com/questions/49/binary-tree-level-order-traversal#answer-container-2543 
     */ 
    public static void levelTraversalRec(TreeNode root) {  
        ArrayList<ArrayList<Integer>> ret = new ArrayList<ArrayList<Integer>>();  
        dfs(root, 0, ret);  
        System.out.println(ret);  
    }  
       
    private static void dfs(TreeNode root, int level, ArrayList<ArrayList<Integer>> ret){  
        if(root == null){  
            return;  
        }  
           
        // 添加一个新的ArrayList表示新的一层  
        if(level >= ret.size()){  
            ret.add(new ArrayList<Integer>());  
        }  
           
        ret.get(level).add(root.val);   // 把节点添加到表示那一层的ArrayList里  
        dfs(root.left, level+1, ret);       // 递归处理下一层的左子树和右子树  
        dfs(root.right, level+1, ret);  
    }  
       
   
    /** 
     * 将二叉查找树变为有序的双向链表 要求不能建立新节点,只调整指针。  
     * 递归解法: 
     * 参考了http://stackoverflow.com/questions/11511898/converting-a-binary-search-tree-to-doubly-linked-list#answer-11530016 
     * 感受是最清晰的递归解法,但要注意递归完,root会在链表的中间位置,所以要手动 
     * 把root移到链表头或链表尾 
     */ 
    public static TreeNode convertBST2DLLRec(TreeNode root) {  
        root = convertBST2DLLSubRec(root);  
           
        // root会在链表的中间位置,所以要手动把root移到链表头  
        while(root.left != null){  
            root = root.left;  
        }  
        return root;  
    }  
       
    /** 
     *  递归转换BST为双向链表(DLL) 
     */ 
    public static TreeNode convertBST2DLLSubRec(TreeNode root){  
        if(root==null || (root.left==null && root.right==null)){  
            return root;  
        }  
           
        TreeNode tmp = null;  
        if(root.left != null){          // 处理左子树  
            tmp = convertBST2DLLSubRec(root.left);  
            while(tmp.right != null){   // 寻找最右节点  
                tmp = tmp.right;  
            }  
            tmp.right = root;       // 把左子树处理后结果和root链接  
            root.left = tmp;  
        }  
        if(root.right != null){     // 处理右子树  
            tmp = convertBST2DLLSubRec(root.right);  
            while(tmp.left != null){    // 寻找最左节点  
                tmp = tmp.left;  
            }  
            tmp.left = root;        // 把右子树处理后结果和root链接  
            root.right = tmp;  
        }  
        return root;  
    }  
       
    /** 
     * 将二叉查找树变为有序的双向链表 迭代解法 
//   * 相似inorder traversal的作法 
     */ 
    public static TreeNode convertBST2DLL(TreeNode root) {  
        if(root == null){  
            return null;  
        }  
        Stack<TreeNode> stack = new Stack<TreeNode>();  
        TreeNode cur = root;        // 指向当前处理节点  
        TreeNode old = null;            // 指向前一个处理的节点  
        TreeNode head = null;       // 链表头  
           
        while( true ){  
            while(cur != null){     // 先添加一个非空节点全部的左孩子到栈  
                stack.push(cur);  
                cur = cur.left;  
            }  
               
            if(stack.isEmpty()){  
                break;  
            }  
                   
            // 由于此时已经没有左孩子了,因此输出栈顶元素  
            cur = stack.pop();  
            if(old != null){  
                old.right = cur;  
            }  
            if(head == null){       // /第一个节点为双向链表头节点  
                head = cur;  
            }  
               
            old = cur;          // 更新old  
            cur = cur.right;    // 准备处理右子树  
        }  
           
        return head;  
    }  
   
    /** 
     * 求二叉树第K层的节点个数   递归解法:  
     * (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那一层)节点数目 
     *  
     * 因此遇到树,先把它拆成左子树和右子树,把问题降解 
     *  
     */ 
    public static int getNodeNumKthLevelRec(TreeNode root, int k) {  
        if (root == null || k < 1) {  
            return 0;  
        }  
   
        if (k == 1) {  
            return 1;  
        }  
        int numLeft = getNodeNumKthLevelRec(root.left, k - 1);      // 求root左子树的k-1层节点数  
        int numRight = getNodeNumKthLevelRec(root.right, k - 1);    // 求root右子树的k-1层节点数  
        return numLeft + numRight;  
    }  
       
    /** 
     *  求二叉树第K层的节点个数   迭代解法:  
     *  同getDepth的迭代解法 
     */ 
    public static int getNodeNumKthLevel(TreeNode root, int k){  
        if(root == null){  
            return 0;  
        }  
        Queue<TreeNode> queue = new LinkedList<TreeNode>();  
        queue.add(root);  
           
        int i = 1;  
        int currentLevelNodes = 1;      // 当前Level,node的数量  
        int nextLevelNodes = 0;         // 下一层Level,node的数量  
           
        while( !queue.isEmpty() && i<k){  
            TreeNode cur = queue.remove();      // 从队头位置移除  
            currentLevelNodes--;            // 减小当前Level node的数量  
            if(cur.left != null){               // 若是有左孩子,加到队尾  
                queue.add(cur.left);  
                nextLevelNodes++;           // 并增长下一层Level node的数量  
            }  
            if(cur.right != null){          // 若是有右孩子,加到队尾  
                queue.add(cur.right);  
                nextLevelNodes++;  
            }  
               
            if(currentLevelNodes == 0){ // 说明已经遍历完当前层的全部节点  
                currentLevelNodes = nextLevelNodes;     // 初始化下一层的遍历  
                nextLevelNodes = 0;  
                i++;            // 进入到下一层  
            }  
        }  
           
        return currentLevelNodes;  
    }  
   
    /** 
     * 求二叉树中叶子节点的个数(递归) 
     */ 
    public static int getNodeNumLeafRec(TreeNode root) {  
        // 当root不存在,返回空  
        if (root == null) {  
            return 0;  
        }  
   
        // 当为叶子节点时返回1  
        if (root.left == null && root.right == null) {  
            return 1;  
        }  
   
        // 把一个树拆成左子树和右子树之和,原理同上一题  
        return getNodeNumLeafRec(root.left) + getNodeNumLeafRec(root.right);  
    }  
       
    /** 
     *  求二叉树中叶子节点的个数(迭代) 
     *  仍是基于Level order traversal 
     */ 
    public static int getNodeNumLeaf(TreeNode root) {  
        if(root == null){  
            return 0;  
        }  
        Queue<TreeNode> queue = new LinkedList<TreeNode>();  
        queue.add(root);  
           
        int leafNodes = 0;              // 记录上一个Level,node的数量  
           
        while( !queue.isEmpty() ){  
            TreeNode cur = queue.remove();      // 从队头位置移除  
            if(cur.left != null){               // 若是有左孩子,加到队尾  
                queue.add(cur.left);  
            }  
            if(cur.right != null){              // 若是有右孩子,加到队尾  
                queue.add(cur.right);  
            }  
            if(cur.left==null && cur.right==null){          // 叶子节点  
                leafNodes++;  
            }  
        }  
           
        return leafNodes;  
    }  
   
    /** 
     * 判断两棵二叉树是否相同的树。 
     * 递归解法:  
     * (1)若是两棵二叉树都为空,返回真 
     * (2)若是两棵二叉树一棵为空,另外一棵不为空,返回假  
     * (3)若是两棵二叉树都不为空,若是对应的左子树和右子树都同构返回真,其余返回假 
     */ 
    public static boolean isSameRec(TreeNode r1, TreeNode r2) {  
        // 若是两棵二叉树都为空,返回真  
        if (r1 == null && r2 == null) {  
            return true;  
        }  
        // 若是两棵二叉树一棵为空,另外一棵不为空,返回假  
        else if (r1 == null || r2 == null) {  
            return false;  
        }  
   
        if(r1.val != r2.val){  
            return false;  
        }  
        boolean leftRes = isSameRec(r1.left, r2.left);      // 比较对应左子树  
        boolean rightRes = isSameRec(r1.right, r2.right); // 比较对应右子树  
        return leftRes && rightRes;  
    }  
       
    /** 
     * 判断两棵二叉树是否相同的树(迭代) 
     * 遍历一遍便可,这里用preorder 
     */ 
    public static boolean isSame(TreeNode r1, TreeNode r2) {  
        // 若是两个树都是空树,则返回true  
        if(r1==null && r2==null){  
            return true;  
        }  
           
        // 若是有一棵树是空树,另外一颗不是,则返回false  
        if(r1==null || r2==null){  
            return false;  
        }  
           
        Stack<TreeNode> s1 = new Stack<TreeNode>();  
        Stack<TreeNode> s2 = new Stack<TreeNode>();  
           
        s1.push(r1);  
        s2.push(r2);  
           
        while(!s1.isEmpty() && !s2.isEmpty()){  
            TreeNode n1 = s1.pop();  
            TreeNode n2 = s2.pop();  
            if(n1==null && n2==null){  
                continue;  
            }else if(n1!=null && n2!=null && n1.val==n2.val){  
                s1.push(n1.right);  
                s1.push(n1.left);  
                s2.push(n2.right);  
                s2.push(n2.left);  
            }else{  
                return false;  
            }  
        }  
        return true;  
    }  
   
    /** 
     * 判断二叉树是否是平衡二叉树 递归解法:  
     * (1)若是二叉树为空,返回真 
     * (2)若是二叉树不为空,若是左子树和右子树都是AVL树而且左子树和右子树高度相差不大于1,返回真,其余返回假 
     */ 
    public static boolean isAVLRec(TreeNode root) {  
        if(root == null){           // 若是二叉树为空,返回真  
            return true;  
        }  
           
        // 若是左子树和右子树高度相差大于1,则非平衡二叉树, getDepthRec()是前面实现过的求树高度的方法  
        if(Math.abs(getDepthRec(root.left) - getDepthRec(root.right)) > 1){  
            return false;  
        }  
           
        // 递归判断左子树和右子树是否为平衡二叉树  
        return isAVLRec(root.left) && isAVLRec(root.right);  
    }  
       
   
    /** 
     * 求二叉树的镜像 递归解法:  
     * (1)若是二叉树为空,返回空 
     * (2)若是二叉树不为空,求左子树和右子树的镜像,而后交换左子树和右子树 
     */ 
    // 1. 破坏原来的树,把原来的树改为其镜像  
    public static TreeNode mirrorRec(TreeNode root) {  
        if (root == null) {  
            return null;  
        }  
   
        TreeNode left = mirrorRec(root.left);  
        TreeNode right = mirrorRec(root.right);  
   
        root.left = right;  
        root.right = left;  
        return root;  
    }  
       
    // 2. 不能破坏原来的树,返回一个新的镜像树  
    public static TreeNode mirrorCopyRec(TreeNode root){  
        if(root == null){  
            return null;  
        }  
           
        TreeNode newNode = new TreeNode(root.val);  
        newNode.left = mirrorCopyRec(root.right);  
        newNode.right = mirrorCopyRec(root.left);  
           
        return newNode;  
    }  
       
    // 3. 判断两个树是否互相镜像  
    public static boolean isMirrorRec(TreeNode r1, TreeNode r2){  
        // 若是两个树都是空树,则返回true  
        if(r1==null && r2==null){  
            return true;  
        }  
           
        // 若是有一棵树是空树,另外一颗不是,则返回false  
        if(r1==null || r2==null){  
            return false;  
        }  
           
        // 若是两个树都非空树,则先比较根节点  
        if(r1.val != r2.val){  
            return false;  
        }  
           
        // 递归比较r1的左子树的镜像是否是r2右子树 和   
        // r1的右子树的镜像是否是r2左子树  
        return isMirrorRec(r1.left, r2.right) && isMirrorRec(r1.right, r2.left);  
    }  
       
    // 1. 破坏原来的树,把原来的树改为其镜像  
    public static void mirror(TreeNode root) {  
        if(root == null){  
            return;  
        }  
           
        Stack<TreeNode> stack = new Stack<TreeNode>();  
        stack.push(root);  
        while( !stack.isEmpty() ){  
            TreeNode cur = stack.pop();  
               
            // 交换左右孩子  
            TreeNode tmp = cur.right;  
            cur.right = cur.left;  
            cur.left = tmp;  
               
            if(cur.right != null){  
                stack.push(cur.right);  
            }  
            if(cur.left != null){  
                stack.push(cur.left);  
            }  
        }  
    }  
       
    // 2. 不能破坏原来的树,返回一个新的镜像树  
    public static TreeNode mirrorCopy(TreeNode root){  
        if(root == null){  
            return null;  
        }  
           
        Stack<TreeNode> stack = new Stack<TreeNode>();  
        Stack<TreeNode> newStack = new Stack<TreeNode>();  
        stack.push(root);  
        TreeNode newRoot = new TreeNode(root.val);  
        newStack.push(newRoot);  
           
        while( !stack.isEmpty() ){  
            TreeNode cur = stack.pop();  
            TreeNode newCur = newStack.pop();  
               
            if(cur.right != null){  
                stack.push(cur.right);  
                newCur.left = new TreeNode(cur.right.val);  
                newStack.push(newCur.left);  
            }  
            if(cur.left != null){  
                stack.push(cur.left);  
                newCur.right = new TreeNode(cur.left.val);  
                newStack.push(newCur.right);  
            }  
        }  
           
        return newRoot;  
    }  
       
   
    /** 
     * 求二叉树中两个节点的最低公共祖先节点  
     * 递归解法:  
     * (1)若是两个节点分别在根节点的左子树和右子树,则返回根节点 
     * (2)若是两个节点都在左子树,则递归处理左子树;若是两个节点都在右子树,则递归处理右子树 
     */ 
    public static TreeNode getLastCommonParentRec(TreeNode root, TreeNode n1, TreeNode n2) {  
        if (findNodeRec(root.left, n1)) {               // 若是n1在树的左子树  
            if (findNodeRec(root.right, n2)) {      // 若是n2在树的右子树  
                return root;                                // 返回根节点  
            } else {            // 若是n2也在树的左子树  
                return getLastCommonParentRec(root.left, n1, n2); // 递归处理  
            }  
        } else {                // 若是n1在树的右子树  
            if (findNodeRec(root.left, n2)) {           // 若是n2在左子树  
                return root;  
            } else {                 // 若是n2在右子树  
                return getLastCommonParentRec(root.right, n1, n2); // 递归处理  
            }  
        }  
    }  
   
    // 帮助方法,递归判断一个点是否在树里  
    private static boolean findNodeRec(TreeNode root, TreeNode node) {  
        if (root == null || node == null) {  
            return false;  
        }  
        if (root == node) {  
            return true;  
        }  
   
        // 先尝试在左子树中查找  
        boolean found = findNodeRec(root.left, node);  
        if (!found) {       // 若是查找不到,再在右子树中查找  
            found = findNodeRec(root.right, node);  
        }  
        return found;  
    }  
       
    // 求二叉树中两个节点的最低公共祖先节点 (更加简洁版的递归)  
    public static TreeNode getLastCommonParentRec2(TreeNode root, TreeNode n1, TreeNode n2) {  
        if(root == null){  
            return null;  
        }  
           
        // 若是有一个match,则说明当前node就是要找的最低公共祖先  
        if(root.equals(n1) || root.equals(n2)){  
            return root;  
        }  
        TreeNode commonInLeft = getLastCommonParentRec2(root.left, n1, n2);  
        TreeNode commonInRight = getLastCommonParentRec2(root.right, n1, n2);  
           
        // 若是一个左子树找到,一个在右子树找到,则说明root是惟一可能的最低公共祖先  
        if(commonInLeft!=null && commonInRight!=null){  
            return root;  
        }  
           
        // 其余状况是要否则在左子树要否则在右子树  
        if(commonInLeft != null){  
            return commonInLeft;  
        }  
           
        return commonInRight;  
    }  
   
    /** 
     * 非递归解法:  
     * 先求从根节点到两个节点的路径,而后再比较对应路径的节点就行,最后一个相同的节点也就是他们在二叉树中的最低公共祖先节点 
     */ 
    public static TreeNode getLastCommonParent(TreeNode root, TreeNode n1, TreeNode n2) {  
        if (root == null || n1 == null || n2 == null) {  
            return null;  
        }  
   
        ArrayList<TreeNode> p1 = new ArrayList<TreeNode>();  
        boolean res1 = getNodePath(root, n1, p1);  
        ArrayList<TreeNode> p2 = new ArrayList<TreeNode>();  
        boolean res2 = getNodePath(root, n2, p2);  
   
        if (!res1 || !res2) {  
            return null;  
        }  
   
        TreeNode last = null;  
        Iterator<TreeNode> iter1 = p1.iterator();  
        Iterator<TreeNode> iter2 = p2.iterator();  
   
        while (iter1.hasNext() && iter2.hasNext()) {  
            TreeNode tmp1 = iter1.next();  
            TreeNode tmp2 = iter2.next();  
            if (tmp1 == tmp2) {  
                last = tmp1;  
            } else { // 直到遇到非公共节点  
                break;  
            }  
        }  
        return last;  
    }  
   
    // 把从根节点到node路径上全部的点都添加到path中  
    private static boolean getNodePath(TreeNode root, TreeNode node, ArrayList<TreeNode> path) {  
        if (root == null) {  
            return false;  
        }  
           
        path.add(root);     // 把这个节点加到路径中  
        if (root == node) {  
            return true;  
        }  
   
        boolean found = false;  
        found = getNodePath(root.left, node, path); // 先在左子树中找  
           
        if (!found) {               // 若是没找到,再在右子树找  
            found = getNodePath(root.right, node, path);  
        }  
        if (!found) {               // 若是实在没找到证实这个节点不在路径中,说明刚才添加进去的不是路径上的节点,删掉!  
            path.remove(root);    
        }  
   
        return found;  
    }  
   
    /** 
     * 求二叉树中节点的最大距离 即二叉树中相距最远的两个节点之间的距离。 (distance / diameter) 
     * 递归解法:  
     * (1)若是二叉树为空,返回0,同时记录左子树和右子树的深度,都为0 
     * (2)若是二叉树不为空,最大距离要么是左子树中的最大距离,要么是右子树中的最大距离, 
     * 要么是左子树节点中到根节点的最大距离+右子树节点中到根节点的最大距离, 
     * 同时记录左子树和右子树节点中到根节点的最大距离。 
     *  
     * http://www.cnblogs.com/miloyip/archive/2010/02/25/1673114.html 
     *  
     * 计算一个二叉树的最大距离有两个状况: 
  
        状况A: 路径通过左子树的最深节点,经过根节点,再到右子树的最深节点。 
        状况B: 路径不穿过根节点,而是左子树或右子树的最大距离路径,取其大者。 
        只须要计算这两个状况的路径距离,并取其大者,就是该二叉树的最大距离 
     */ 
    public static Result getMaxDistanceRec(TreeNode root){  
        if(root == null){  
            Result empty = new Result(0, -1);       // 目的是让调用方 +1 后,把当前的不存在的 (NULL) 子树当成最大深度为 0  
            return empty;  
        }  
           
        // 计算出左右子树分别最大距离  
        Result lmd = getMaxDistanceRec(root.left);  
        Result rmd = getMaxDistanceRec(root.right);  
           
        Result res = new Result();  
        res.maxDepth = Math.max(lmd.maxDepth, rmd.maxDepth) + 1;        // 当前最大深度  
        // 取状况A和状况B中较大值  
        res.maxDistance = Math.max( lmd.maxDepth+rmd.maxDepth, Math.max(lmd.maxDistance, rmd.maxDistance) );  
        return res;  
    }  
       
    private static class Result{  
        int maxDistance;  
        int maxDepth;  
        public Result() {  
        }  
   
        public Result(int maxDistance, int maxDepth) {  
            this.maxDistance = maxDistance;  
            this.maxDepth = maxDepth;  
        }  
    }  
       
    /** 
     * 13. 由前序遍历序列和中序遍历序列重建二叉树(递归) 
     * 感受这篇是讲的最为清晰的: 
     * http://crackinterviewtoday.wordpress.com/2010/03/15/rebuild-a-binary-tree-from-inorder-and-preorder-traversals/ 
     * 文中还提到一种避免开额外空间的方法,等下次补上 
     */ 
    public static TreeNode rebuildBinaryTreeRec(List<Integer> preOrder, List<Integer> inOrder){  
        TreeNode root = null;  
        List<Integer> leftPreOrder;  
        List<Integer> rightPreOrder;  
        List<Integer> leftInorder;  
        List<Integer> rightInorder;  
        int inorderPos;  
        int preorderPos;  
    
        if ((preOrder.size() != 0) && (inOrder.size() != 0))  
        {  
            // 把preorder的第一个元素做为root  
            root = new TreeNode(preOrder.get(0));  
    
            //  Based upon the current node data seperate the traversals into leftPreorder, rightPreorder,  
            //  leftInorder, rightInorder lists  
            // 由于知道root节点了,因此根据root节点位置,把preorder,inorder分别划分为 root左侧 和 右侧 的两个子区间  
            inorderPos = inOrder.indexOf(preOrder.get(0));      // inorder序列的分割点  
            leftInorder = inOrder.subList(0, inorderPos);  
            rightInorder = inOrder.subList(inorderPos + 1, inOrder.size());  
    
            preorderPos = leftInorder.size();                           // preorder序列的分割点  
            leftPreOrder = preOrder.subList(1, preorderPos + 1);  
            rightPreOrder = preOrder.subList(preorderPos + 1, preOrder.size());  
    
            root.left = rebuildBinaryTreeRec(leftPreOrder, leftInorder);        // root的左子树就是preorder和inorder的左侧区间而造成的树  
            root.right = rebuildBinaryTreeRec(rightPreOrder, rightInorder); // root的右子树就是preorder和inorder的右侧区间而造成的树  
        }  
    
        return root;  
    }  
       
    /** 
        14.  判断二叉树是否是彻底二叉树(迭代) 
        若设二叉树的深度为h,除第 h 层外,其它各层 (1~h-1) 的结点数都达到最大个数, 
        第 h 层全部的结点都连续集中在最左边,这就是彻底二叉树。 
        有以下算法,按层次(从上到下,从左到右)遍历二叉树,当遇到一个节点的左子树为空时, 
        则该节点右子树必须为空,且后面遍历的节点左右子树都必须为空,不然不是彻底二叉树。 
     */ 
    public static boolean isCompleteBinaryTree(TreeNode root){  
        if(root == null){  
            return false;  
        }  
           
        Queue<TreeNode> queue = new LinkedList<TreeNode>();  
        queue.add(root);  
        boolean mustHaveNoChild = false;  
        boolean result = true;  
           
        while( !queue.isEmpty() ){  
            TreeNode cur = queue.remove();  
            if(mustHaveNoChild){    // 已经出现了有空子树的节点了,后面出现的必须为叶节点(左右子树都为空)    
                if(cur.left!=null || cur.right!=null){  
                    result = false;  
                    break;  
                }  
            } else {  
                if(cur.left!=null && cur.right!=null){      // 若是左子树和右子树都非空,则继续遍历  
                    queue.add(cur.left);  
                    queue.add(cur.right);  
                }else if(cur.left!=null && cur.right==null){    // 若是左子树非空但右子树为空,说明已经出现空节点,以后必须都为空子树  
                    mustHaveNoChild = true;  
                    queue.add(cur.left);  
                }else if(cur.left==null && cur.right!=null){    // 若是左子树为空但右子树非空,说明这棵树已经不是彻底二叉彻底树!  
                    result = false;  
                    break;  
                }else{          // 若是左右子树都为空,则后面的必须也都为空子树  
                    mustHaveNoChild = true;  
                }  
            }  
        }  
        return result;  
    }  
       
    /** 
     * 14.  判断二叉树是否是彻底二叉树(递归) 
     * http://stackoverflow.com/questions/1442674/how-to-determine-whether-a-binary-tree-is-complete 
     *  
     */ 
    public static boolean isCompleteBinaryTreeRec(TreeNode root){  
//      Pair notComplete = new Pair(-1, false);  
//      return !isCompleteBinaryTreeSubRec(root).equalsTo(notComplete);  
        return isCompleteBinaryTreeSubRec(root).height != -1;  
    }  
       
    // 递归判断是否满树(完美)  
    public static boolean isPerfectBinaryTreeRec(TreeNode root){  
        return isCompleteBinaryTreeSubRec(root).isFull;  
    }  
       
    // 递归,要建立一个Pair class来保存树的高度和是否已满的信息  
    public static Pair isCompleteBinaryTreeSubRec(TreeNode root){  
        if(root == null){  
            return new Pair(0, true);  
        }  
           
        Pair left = isCompleteBinaryTreeSubRec(root.left);  
        Pair right = isCompleteBinaryTreeSubRec(root.right);  
           
        // 左树满节点,并且左右树相同高度,则是惟一可能造成满树(若右树也是满节点)的状况  
        if(left.isFull && left.height==right.height){  
            return new Pair(1+left.height, right.isFull);  
        }  
           
        // 左树非满,但右树是满节点,且左树高度比右树高一  
        // 注意到若是其左树为非彻底树,则它的高度已经被设置成-1,  
        // 所以不可能知足第二个条件!  
        if(right.isFull && left.height==right.height+1){  
            return new Pair(1+left.height, false);  
        }  
           
        // 其余状况都是非彻底树,直接设置高度为-1  
        return new Pair(-1, false);  
    }  
       
    private static class Pair{  
        int height;             // 树的高度  
        boolean isFull;     // 是不是个满树  
   
        public Pair(int height, boolean isFull) {  
            this.height = height;  
            this.isFull = isFull;  
        }  
   
        public boolean equalsTo(Pair obj){  
            return this.height==obj.height && this.isFull==obj.isFull;  
        }  
    }  
       
}

 

 

一,问题描述html

在控制台上输入一组数据,请按照输入的数据的格式来构造一棵二叉树,并打印出二叉树的高度。java

输入的数据格式以下:node

第一行为一个整数N(实际上是二叉树中边的数目),表示接下来一共有N行输入,每行输入有两个数,左边的数表示父结点,右边的数表示父结点的孩子结点。示例以下:面试

6算法

0 1wordpress

0 2post

1 3ui

2 4this

2 5spa

4 6

从上面的输入能够看出:①根结点0 的左孩子为1,右孩子为2 。②结点1 只有一个孩子,即左孩子3

 

二,问题分析

问题的关键是根据上面的输入数据 构造一棵二叉树。

首先用一个Map<Integer, List<Integer>>保存上面的输入的数据。其中Key为父结点,Value为父结点的孩子结点。对于二叉树而言,父结点的孩子结点最多只有2个,故List长度最大为2.

 

而后,根据Map来构造二叉树便可。

对于Map中的每个Entry,Entry的Key为父结点,找到父结点在树中的位置(findNode方法)。

Entry的Value为父结点的左右孩子,遍历Value,构造孩子结点。已知了父结点在树中的位置,又构造了孩子结点,只须要将父结点的左右指针指向左右孩子便可。

 

三,代码实现

复制代码

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Scanner;
import java.util.Set;

//按要求构造二叉树,假设头结点为0
public class BuildTree {

    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        //<parent, childList>
        Map<Integer, List<Integer>> datas = new LinkedHashMap<Integer, List<Integer>>();
        while(sc.hasNextInt())
        {
            int edges = sc.nextInt();
            
            //将树的信息保存到hashmap<parent, childList>中
            for(int i = 0; i < edges; i++)
            {
                int parent = sc.nextInt();
                int child = sc.nextInt();
                
                if(!datas.containsKey(parent)){
                    List<Integer> childs = new ArrayList<Integer>();
                    childs.add(child);
                    datas.put(parent, childs);
                }else{
                    List<Integer> childs = datas.get(parent);
                    childs.add(child);
                }
            }//end for
            BinaryNode root = buildTree(datas);
            
            int height = height(root);
            System.out.println(height);
        }
        sc.close();
    }
    
    //求二叉树的高度
    private static int height(BinaryNode root){
        
        if(root == null)
            return 0;
        int leftHeight = height(root.left);
        int rightHeight = height(root.right);
        
        return 1 + (leftHeight > rightHeight ? leftHeight : rightHeight);
    }
    
    //构造二叉树
    private static BinaryNode buildTree(Map<Integer, List<Integer>> datas){
        BinaryNode root = null;
        
        BinaryNode current = null;
        List<Integer> childs = null;
        Set<Entry<Integer, List<Integer>>> entrySet = datas.entrySet();
        for (Entry<Integer, List<Integer>> entry : entrySet) {
            int parent = entry.getKey();
            current = findNode(parent, root);
            childs = datas.get(parent);
            if(current == null){//说明parent是根结点
                root = new BinaryNode(parent);
                createNode(root, childs);
            }else{
                createNode(current, childs);
            }
        }
        return root;
    }
    
    //建立parent结点的左右孩子结点
    private static void  createNode(BinaryNode parent, List<Integer> childs){
        if(childs.size() == 2){//说明有左右孩子
            BinaryNode leftChild = new BinaryNode(childs.get(0));
            BinaryNode rightChild = new BinaryNode(childs.get(1));
            parent.left = leftChild;
            parent.right = rightChild;
        }
        if(childs.size() == 1){//说明只有左孩子
            BinaryNode leftChild = new BinaryNode(childs.get(0));
            parent.left = leftChild;
        }
    }
    
    //查找树根为root的二叉树中 值为 nodeVal 的结点
    private static BinaryNode findNode(int nodeVal, BinaryNode root){
        //先序递归遍历查找 值为 nodeVal的结点
        BinaryNode target = null;
        
        if(root == null)
            return null;
        if(root.val == nodeVal)
            return root;
        target = findNode(nodeVal, root.left);//先在左子树中查找
        if(target == null)
            target = findNode(nodeVal, root.right);//左子树中未找到,则在右子树中查找
        return target;
    }
    
    private static class BinaryNode{
        int val;
        BinaryNode left;
        BinaryNode right;
        
        public BinaryNode(int val){
            this.val = val;
            left = right = null;
        }
    }
}

复制代码

 

复杂度分析:因为 当构造父结点的左右孩子时,须要先查找父结点在二叉树中的位置,这个查找是用“先序遍历的思路”实现的。

故构造树的时间复杂度为O(1+2+3+……+N)=O(N^2)。有点大。

相关文章
相关标签/搜索