Java算法体系学习(八)二叉树的相关问题求解(树的最大宽度,完全二叉树的判断,中序遍历的后继节点,多叉树与二叉树的转换,折纸问题)

5、求树的最大宽度

  • 思路:在层序遍历的基础上,多用三个变量,一个记录当前层的最后一个位置,一个记录下一层的最后一个位置,一个记录当前层的宽度,这样每层的结束时机遍历完都知道。每次遍历一层更新迭代即可。
  • 为什么需要当前层 和 下一层的最后一个位置都需要记录。因为每次当前层来到最后一个位置时,就可以知道下一层应该开始了,那么宽度就可以重新设置。同时也可以更新下一层的结束位置。
package tree;

import java.util.LinkedList;
import java.util.Queue;

public class Code05_MaxWidth {
    public static int maxWidth(TreeNode root){
        if (root == null){
            return 0;
        }

        Queue<TreeNode> queue = new LinkedList<>();
        TreeNode cur = root;
        TreeNode curEnd = root;
        TreeNode nextEnd = root;
        int count = 0;
        int width = 1;
        queue.add(cur);
        while (!queue.isEmpty()){
            cur = queue.poll();
            count++;
            if (cur.left != null){
                //每次都更新nextend,那么当cur来到curend时,nexrend会被正确更新
                queue.add(cur.left);
                nextEnd = cur.left;
            }
            if(cur.right != null){
                queue.add(cur.right);
                nextEnd = curEnd.right;
            }
            if(cur == curEnd){
                width = Math.max(width,count);
                count = 0;
                curEnd = nextEnd;
            }
        }
        return width;
    }
}

6、折纸问题(二叉树思想启蒙)

  • 把一段纸条竖着放在桌子上,然后从纸条的下边向上方对折1次,压出折痕后展开。此时 折痕是凹下去的,即折痕突起的方向指向纸条的背面。如果从纸条的下边向上方连续对折2 次,压出折痕后展开,此时有三条折痕,从上到下依次是下折痕、下折痕和上折痕。
  • 给定一 个输入参数N,代表纸条都从下边向上方连续对折N次,请从上到下打印所有折痕的方向 例如:N=1时,打印: down;N=2时,打印: down down up
  • 将每个折痕代表为树种的每一个节点,可以看到根节点为down,然后每一个节点的左孩子为down,右孩子为up,而N即为二叉树的层数,由此可以用二叉树的中序遍历进行打印
  • 我们实际不构建二叉树,因为太浪费空间。用一个boolean(isLeft)代表是左孩子还是右孩子。如果是左孩子就打印down,否则打印up。由于根节点打印down,所以第一次递归传入为true。
package tree;

public class Code06_PaperFolding {
    public static void printAll(int n){
        process(1,n,true);
    }

    /**
     * i为当前来到第几层,n为总的层数
     * 这里不构建实际的二叉树,用一个boolean代表左右孩子
     * @param i
     * @param n
     * @param isLeft
     * @return
     */
    public static void process(int i,int n,boolean isLeft){
        if(i > n){
            return;
        }
        process(i + 1, n, true);
        System.out.print(isLeft ? "down - " : "up - ");
        process(i + 1, n, false);
    }
}

7、判断一颗树是不是完全二叉树

  • 完全二叉树:要么每一层都是满的,要么除了最后一层不满且孩子节点依次从左向右,满足这两种情况称为完全二叉树。
  • 思路很简单:对二叉树进行层序遍历,不满足完全二叉树只有两种情况。
    1. 左右孩子不全且后序节点子树不为null
    2. 左孩子为null但有右孩子
  • 所以对二叉树进行层序遍历,在遍历过程中判断这两种情况不满足直接退出
package tree;

import java.util.LinkedList;
import java.util.Queue;

public class Code07_IsCBTByLevel {
    public static boolean isCBT(TreeNode head) {
        if (head == null) {
            return true;
        }

        Queue<TreeNode> queue = new LinkedList<>();
        TreeNode cur = null;
        queue.add(head);
        boolean childrenLess = false;
        while (!queue.isEmpty()) {
            cur = queue.poll();
            if (
                    (childrenLess && (cur.left != null || cur.right != null))
                            || (cur.left == null && cur.right != null)
            ) {
                return false;
            }
            if (cur.left != null){
                queue.add(cur.left);
            }
            if(cur.right != null){
                queue.add(cur.right);
            }
            if (cur.right == null || cur.left ==null){
                childrenLess = true;
            }
        }
        return true;
    }
    
}

8、多叉树与二叉树的互换(需要理解递归过程)

8.1 多叉树转二叉树

  • 对于每一个节点来说,把它的所有孩子节点依次串在左树的右边界上
    public static class  MultiwayTree{
        public int value;
        public ArrayList<MultiwayTree> children;

        public MultiwayTree(int value) {
            this.value = value;
            children = new ArrayList<>();
        }

        public MultiwayTree(int value, ArrayList<MultiwayTree> children) {
            this.value = value;
            this.children = children;
        }
    }

    /**
     * 多叉树转二叉树
     * @param multiwayHead
     * @return
     */
    public static TreeNode encode(MultiwayTree multiwayHead){
        if (multiwayHead == null){
            return null;
        }
        return en(multiwayHead.children);
    }

    public static TreeNode en(List<MultiwayTree> children){
        TreeNode head = null;
        TreeNode cur = null;
        for (MultiwayTree child : children){
            TreeNode tNode = new TreeNode(child.value);
            if (head == null){
                head = tNode;
            }else {
                cur.right = tNode;
            }
            cur = tNode;
            cur.left = en(child.children);
        }
        return head;
    }

8.2 二叉树转多叉树

  • 遍历每个节点左子树的右边界,依次作为该节点的孩子
    /**
     * 二叉树转多叉树
     * @param head
     * @return
     */
    public static MultiwayTree decode(TreeNode head){
        if(head == null){
            return null;
        }
        return new MultiwayTree(head.value,de(head.left));
    }

    /**
     * 返回传入node转换后的孩子列表
     * @param node
     * @return
     */
    public static List<MultiwayTree> de(TreeNode node){
        List<MultiwayTree> children = new ArrayList<>();
        while (node != null){
            MultiwayTree cur = new MultiwayTree(node.value,de(node.left));
            children.add(cur);
            node = node.right;
        }
        return children;
    }

9、给定一个二叉树节点,其中节点指针还包括它的父指针,寻找它中序遍历情况下的下一个节点

  • 结合前面二叉树的中序遍历非递归实现可知,如果某个节点有右树,那么它的下一个位置必定为其右子树的最左节点
  • 如果它没有右孩子,那么它将会沿着parent指针一直走上去,直到parent为某一个节点的左孩子或者来到根节点,那么下一个位置将会是parent。
package tree;

public class Code09_GetSuccessorNodeWithIn {
    public static class Node{
        public int value;
        public Node left;
        public Node right;
        public Node parent;

        public Node(int value) {
            this.value = value;
        }
    }

    public Node getSuccessorNode(Node head){
        if (head == null){
            return null;
        }

        Node cur = head;
        //右子树不为空
        if (cur.right != null){
            cur = cur.left;
            while (cur.left != null){
                cur = cur.left;
            }
            return cur;
        }else {
            //右子树为空,找打它的父亲节点,直到父亲节点来到head或者父亲节点为某个节点的左子树
            //只有head位置的parent为null,所以parent为null就来到了头结点
            Node parent = cur.parent;
            while (parent != null && parent.right == cur){
                cur = parent;
                parent = cur.parent;
            }
            return parent;
        }
    }
}

10、证明先序遍历x的左边和后序遍历x的右边的交集仅仅是x的祖先节点

  • 对于x的父节点

  • 先序遍历-: 根---->左---->-右—> x的左边必包括父亲节点

  • 后序遍历-: 左---->右---->-根—> x的右边必包括父亲节点

  • 对于x的孩子节点

  • 先序遍历,x的孩子节点全出现在右边

  • 后序遍历,x的孩子节点全出现在左边

  • 对于x的兄弟节点

  • 先序遍历

    • x作为左树,兄弟节点全在右边
    • x作为右树,兄弟节点全在左边
  • 后序遍历

    • x作为左树,兄弟节点全在右边
    • x作为右树,兄弟节点全在左边
  • 对x先序左边和x后序的右边取交集,发现只有父亲节点符合条件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值