数据结构之二叉树总篇(Java)

前言

  • 面试中的树都是二叉树,即有左右两个节点的树
  • 牢记:root.left表示左子树,root.right表示右子树,通过树的递归解决问题

二叉树定义

public class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    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;
    } 

前序遍历

即:先根遍历

递归

public static void preorderTraversalRec(TreeNode root) {
        if (root == null) {
            return;
        }
        System.out.print(root.val + " ");
        preorderTraversalRec(root.left);
        preorderTraversalRec(root.right);
    }  

非递归:

递归->非递归

利用栈

创建栈
将根节点压入栈
While 栈非空
    弹出一个节点
    打印它的值
    If 存在右节点,将右节点压入栈
    IF 存在左节点,将左节点压入栈

代码实现:

/**
     *  前序遍历迭代解法:用一个辅助stack,总是把右孩子放进栈
     *
     */
    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.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 + " ");
    }  

非递归

  1. 设置两个栈stk, stk2;
  2. 将根结点压入第一个栈stk;
  3. 弹出stk栈顶的结点,并把该结点压入第二个栈stk2;
  4. 将当前结点的左孩子和右孩子先后分别入栈stk;
  5. 当所有元素都压入stk2后,依次弹出stk2的栈顶结点,并访问之。

第一个栈的入栈顺序是:根结点,左孩子和右孩子;于是,压入第二个栈的顺序是:根结点,右孩子和左孩子。因此,弹出的顺序就是:左孩子,右孩子和根结点。

/**
     *  后序遍历迭代解法
     *  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 + " ");
        }
    }  

复杂度分析

二叉树遍历的非递归实现,每个结点只需遍历一次,故时间复杂度为O(n)。而使用了栈,空间复杂度为二叉树的高度,故空间复杂度为O(n)。

层次遍历

非递归:

 /**
     * 分层遍历二叉树(按层次从上往下,从左往右)迭代
     * 相当于广度优先搜索,使用队列实现。队列初始化,将根节点压入队列。当队列不为空,进行如下操作:弹出一个节点
     * ,访问,若左子节点或右子节点不为空,将其压入队列
     */
    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);
    }  

二叉树转为有序的双向链表

输入一棵二叉搜索树,将该二叉搜索树转换成一个排序的双向链表。要求不能创建任何新的结点,只能调整树中结点指针的指向。

https://www.nowcoder.com/practice/947f6eb80d944a84850b0538bf0ec3a5?tpId=13&tqId=11179&rp=2&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking

递归:

public class Solution {
    TreeNode phead;  //指针
    TreeNode realhead;  //头指针
    public TreeNode Convert(TreeNode pRootOfTree) {

        if(pRootOfTree==null)
            return pRootOfTree;

        ConvertSub(pRootOfTree);

        return realhead;
    }

    //中序遍历
    public void ConvertSub(TreeNode root){
        if(root==null)
            return ;
        ConvertSub(root.left);
        if(phead==null){
            phead=root;
            realhead=root;
        }
        else{
            phead.right=root;
            root.left=phead;
            phead=root;
        }
        ConvertSub(root.right);
    }

}

非递归:迭代

/**
     * 将二叉查找树变为有序的双向链表 迭代解法
//   * 类似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层的节点个数*

递归

 /**
     * 求二叉树第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;
    }  

求二叉树中节点的最大距离

 /**
     * 求二叉树中节点的最大距离 即二叉树中相距最远的两个节点之间的距离。 (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;
        }
    }  

判断二叉树是不是完全二叉树

非递归迭代

 /**
        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;
        }
    }  

二叉树转堆

问题:给定一个存储于一个无序的二叉树中的整数集合。使用数组排序算法将这个树转化为一个堆,这个堆使用一个平衡二叉树作为底层的数据结构。

参考

其他参考

时间: 2024-10-13 01:48:10

数据结构之二叉树总篇(Java)的相关文章

java数据结构:二叉树

树:树形结构的特点是一个节点可有多个直接后继,是一种常用的非线性结构. 二叉树:结合了有序数组和链表的优点,在树中查找数据和在有序数组中查找一样快,增删数据和在链表中一样快. 插入操作时,二叉树从根节点开始,比父节点大的往左边插入,比父节点小的往右边插入 下面是链表实现二叉树: Node.java package BTree; /** * 链表二叉树的节点类 */ public class Node { int data; Node leftChild; Node rightChild; //初

第10篇-JAVA 集合框架-JAVA 泛型

第10篇-JAVA 集合框架-JAVA 泛型 每篇一句 :所有的不甘,都是因为还心存梦想 初学心得: 不是每件事都注定会成功,但是每件事都值得一试 (笔者:JEEP/711)[JAVA笔记 | 时间:2017-04-15| JAVA 集合框架/JAVA 泛型 ] 1.JAVA 集合框架概念 通俗的说,集合就是一个存放数据的容器,准确的说,就是放数据对象引用的容器 数组和集合都是容器,有何不同? 数组长度固定,集合长度可变 数组只能存放相同类型的数据,集合可以存放不同类型的数据 数组可存放简单数据

最全Pycharm教程(14)——Pycharm编辑器功能总篇

最全Pycharm教程(1)--定制外观 最全Pycharm教程(2)--代码风格 最全Pycharm教程(3)--代码的调试.运行 最全Pycharm教程(4)--有关Python解释器的相关配置 最全Pycharm教程(5)--Python快捷键相关设置 最全Pycharm教程(6)--将Pycharm作为Vim编辑器使用 最全Pycharm教程(7)--虚拟机VM的配置 最全Pycharm教程(8)--Django工程的创建和管理 最全Pycharm教程(9)--创建并运行一个基本的Pyt

【算法与数据结构】二叉树的 中序 遍历

前一篇写了二叉树的先序遍历,本篇记录一下二叉树的中序遍历,主要是非递归形式的中序遍历. 由于距离上篇有好几天了,所以这里把二叉树的创建和存储结构也重复的写了一遍. 二叉树如下 二叉树的存储方式依然是二叉链表方式,其结构如下 typedef struct _tagBinTree { unsigned char value; struct _tagBinTree* left; struct _tagBinTree* right; }BinTree, *PBinTree; 先序递归形式的创建二叉树代码

哈夫曼树(最优二叉树)及其Java实现

一.定义 一些定义: 节点之间的路径长度:在树中从一个结点到另一个结点所经历的分支,构成了这两个结点间的路径上的经过的分支数称为它的路径长度 树的路径长度:从树的根节点到树中每一结点的路径长度之和.在结点数目相同的二叉树中,完全二叉树的路径长度最短. 结点的权:在一些应用中,赋予树中结点的一个有某种意义的实数. 结点的带权路径长度:结点到树根之间的路径长度与该结点上权的乘积. 树的带权路径长度(Weighted Path Length of Tree:WPL):定义为树中所有叶子结点的带权路径长

二叉树——遍历篇(c++)

二叉树--遍历篇 二叉树很多算法题都与其遍历相关,笔者经过大量学习并进行了思考和总结,写下这篇二叉树的遍历篇. 1.二叉树数据结构及访问函数 #include <stdio.h> #include <iostream> #include <stack> using namespace std; struct BTNode { int value; struct BTNode *left, *right; BTNode(int value_) :value(value_)

第3篇-JAVA基础

第3篇-JAVA基础 每篇一句 :目标是给梦想一个期限,行动与坚持就是实现梦想的过程 初学心得: 遇到困难或问题,它不是休止符,而是引向你如何解决问题的标识 (笔者:JEEP/711)[JAVA笔记 | 时间:2017-03-26| JAVA基础 Ⅱ] 上篇回顾 上篇文章中我们学习了JAVA底层的运行机制与深入剖析以及解释其中JAVA基础代码的含义 本篇文章将JAVA基础Ⅱ全面剖析解释,因为JAVA基础非常重要,务必要牢记知识点!!! 1.JAVA基础语法格式 JAVA采用unicode编码 1

第14篇-JAVA GUI编程

第14篇-JAVA GUI编程 每篇一句 :道路一开始开辟的时候总是存在障碍的 初学心得: 原本下定决心才能开始的事情也变得理所当然 (笔者:JEEP/711)[JAVA笔记 | 时间:2017-04-25| JAVA GUI编程 ] 1.什么是界面 图形用户界面(Graphical User Interface,简称 GUI,又称图形用户接口)是指采用图形方式显示的计算机操作用户界面 与早期计算机使用的命令行界面相比,图形界面对于用户来说在视觉上更易于接受 2.Java 基础类 JFC 的基本

第16篇-JAVA 类加载与反射

第16篇-JAVA 类加载与反射 每篇一句 :敢于弯曲,是为了更坚定的站立 初学心得: 追求远中的欢声笑语,追求远中的结伴同行 (笔者:JEEP/711)[JAVA笔记 | 时间:2017-05-12| JAVA 类加载与反射 ] 1.类加载 类加载器负责将 .class 文件(可能在磁盘上, 也可能在网络上) 加载到内存中, 并为之生成对应的 java.lang.Class 对象 当程序主动使用某个类时,如果该类还未被加载到内存中,系统会通过加载.连接.初始化三个步骤来对该类进行初始化,如果没