二叉树一些题目和解题思路

本文介绍了如何将多叉树序列化为二叉树并进行反序列化,以及如何计算节点后续节点、判断树的完全性、平衡性和搜索性,以及涉及的相关树形动态规划问题的通用思路。
摘要由CSDN通过智能技术生成

1.Encode N-ary Tree to Binary Tree

一个多叉树,将其序列化为一个二叉树,并且可以通过该二叉树反序列化为原来的多叉树

思路:

我们可以将该多叉树的每个子节点,全部放置在该节点的左数右边界上,如下图:

我们可以将该节点的所有子节点,放置在该节点的左树右边界上,右树我们不管,可以为null,这样我们就可以将该多叉树序列化成唯一的二叉树,将该步骤反过来就可以将该二叉树还原成原来的多叉树。

代码如下:

public class EncodeNaryTreeToBinaryTree {
    public static class Node{
        public int val;
        public List<Node> children;

        public Node(){

        }
        public Node(int _val){
            val = _val;
        }
        public Node(int _val,List<Node> _children){
            val = _val;
            children = _children;
        }
    }

    public static class TreeNode{
        int val;
        TreeNode left;
        TreeNode right;

        TreeNode(int x){
            val = x;
        }
    }


    class Codec{
        public TreeNode encode(Node root){
           if (root == null){
               return  null;
           }
           TreeNode head = new TreeNode(root.val);
           head.left = en(root.children);
           return head;
        }
        public TreeNode en (List<Node> childrens){
            //定义二叉树当前节点
            TreeNode cur = null;
            //定义一个头节点,作为左子树右列的头
            TreeNode head = null;
            for (Node children : childrens) {
                TreeNode node = new TreeNode(children.val);
                //如果为空,则表示右列还没有节点,我们就new一个
                //不为空说明左子树的右列已经有节点了,我们就连下去
                if (head == null){
                    head = node;
                }else {
                    cur.right = node;
                }
                cur = node;
                //递归将孩子序列化成二叉树
                cur.left = en(children.children);
            }
            return head;
        }


        public Node decode(TreeNode root){
            if (root == null){
                return null;
            }
            Node node = new Node(root.val,de(root));
            return node;
        }
        public List<Node> de(TreeNode root){
            //新建一个孩子链表
            List<Node> children = new LinkedList<>();
            while (root != null){
              //递归调用生成多叉树的节点  
              Node node = new Node(root.val,de(root.left));
              //将生成的节点添加到链表中
              children.add(node);
              root = root.right;
            }
            return children;
        }
    }
}

2.给定一个二叉树的节点,求出该节点的后续节点

1. 该节点有右子树,该节点的后续节点就是右子树的最左节点。

2.该节点没有右子树,就找该节点的父节点,如果该节点是该父节点的右树,就继续往上,一直到该树是父节点的左树为止,那么该父节点就是该节点的后继节点。

代码如下:

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

        public Node(int data){
            this.value = data;
        }
    }
    public static Node getNextNode(Node node){
        if (node == null){
            return null;
        }
        if (node.right != null){
            return getLeftMost(node.right);
        }else {   //无右子树
            Node parent = node.parent;
            while (parent != null && node == parent.right){
                node = parent;
                parent = node.parent;
            }
            return parent;

        }
    }

    private static Node getLeftMost(Node node) {
        if (node == null){
            return null;
        }
        while (node.left != null){
            node = node.left;
        }
        return node;
    }
}

3.判断该树是否是完全二叉树

3.1 什么是完全二叉树?

二叉树的每一层要么是满的,如果不满,那么一定是最后一层不满,并且从左至右依次处于变满的路上的二叉树叫完全二叉树。

那么如何判断该树是否是完全二叉树呢,其实就是上篇文章中的二叉树按层遍历,只不过在遍历过程中需要遵循一些规则。

那么需要遵循什么规则呢?

1 如果一个节点,该节点如果只有右节点而没有左节点,那么就直接返回false

2.在不满足1的前提下,在第一次遇到某个节点的左节点或者右节点不双全的情况下,之后遍历的节点一定都是叶子节点。否则返回false

如果遍历完之后都没返回false,就说明是完全二叉树。

代码如下:

public static Boolean isCBT(Node head){
        if (head == null){
            return true;
        }
        Queue<Node> queue = new LinkedList<>();
        queue.add(head);
        Boolean leaf  = false;
        Node left = null;
        Node right = null;
        while (!queue.isEmpty()){
            Node poll = queue.poll();
            left = poll.left;
            right = poll.right;
            if (leaf && (left != null || right!= null) || (left== null && right != null)){
                return false;
            }

            if (leaf != null || right != null){
                leaf = true;
            }

            if (left != null){
                queue.add(left);
            }
            if (right != null){
                queue.add(right);
            }

        }
        return true;
    }

4.返回该树是否是平衡二叉树

4.1  平衡二叉树是指该树的每一颗子树,它的左树的最大高度和右树的最大高度相差的绝对值不超过1。

代码如下:

public static Boolean isBalanceTree(Node head){
        if (head == null){
            return true;
        }
        return process(head).isBalance;
    }

    public static class Info{
        boolean isBalance;
        int height;
        public Info(boolean isBalance,int height){
            this.isBalance = isBalance;
            this.height = height;
        }
    }

    public static Info process(Node node){
        if (node == null){
            return new Info(true,0);
        }

        Info left = process(node.left);
        Info right = process(node.right);
        boolean isBalance = true;
        if (!left.isBalance || !right.isBalance){
            isBalance = false;
        }
        if (Math.abs(left.height - right.height) > 1){
            isBalance = false;
        }
        return new Info(isBalance, Math.max(left.height,right.height)+1);
    }

5.返回该树是否是搜索二叉树

5.1 什么是搜索二叉树

对于搜索二叉树的每一个子树,它左子树的最大值小于该节点的值,它右子树的最小值大于该节点的值。

代码如下:

public static boolean isSearchTree(Node head){
        if (head == null){
            return true;
        }
        return process(head).isSearch;
    }

    public static class Info{
        boolean isSearch;
        int max;
        int min;

        public Info(boolean isSearch,int max,int min){
            this.isSearch = isSearch;
            this.max = max;
            this.min = min;

        }
    }

    public static Info process(Node node){
        if (node == null){
            return null;
        }

        Info left = process(node.left);
        Info right = process(node.right);

        boolean isSearch = true;

        if (left != null && !left.isSearch){
            isSearch = false;
        }
        if (right != null && !right.isSearch){
            isSearch = false;
        }

        int min = node.value;
        int max = node.value;
        if (left != null){
            max = Math.max(left.max,max);
            min = Math.min(left.min,min);
        }
        if (right != null){
            max = Math.max(right.max,max);
            min = Math.min(right.min,min);
        }
        if (left != null && left.max >= node.value){
            isSearch = false;
        }
        if (right != null && right.min <= node.value){
            isSearch = false;
        }
        return new Info(isSearch,max,min);
    }

6.返回整棵树的最大距离

如何返回?

1.有头结点参与

返回左树高度和右树的高度+1,如果有头结点参与,那么最远的肯定是左树的高度和右树的高度+1;

2.没有头结点参与

返回左树最大距离或者右树最大距离的较大者

代码如下:

public static int maxDistance(Node head){
        if (head == null){
            return  0;
        }
        return process(head).maxDistance;
    }

    public static class Info{
        int maxDistance;
        int height;

        public Info(int maxDistance,int height) {
            this.maxDistance = maxDistance;
            this.height = height;
        }
    }

    public static Info process(Node node){
        if (node == null){
            return new Info(0,0);
        }
        Info left = process(node.left);
        Info right = process(node.right);
        int height = Math.max(left.height,right.height) + 1;
        int p1 = left.maxDistance;
        int p2 = right.maxDistance;
        int p3 = left.height + right.height + 1;
        int maxDistance = Math.max(p1,Math.max(p2,p3));
        return new Info(maxDistance,height);
    }

7.找到最大的子树是搜索二叉树

通过上文我们已经知道什么是搜索二叉树,该问题是在一个树中,找到最大的搜索二叉树。

public  static int  maxSearchTree(Node head){
        if (head == null){
            return 0;
        }
        return process(head).searchSize;
    }
    public static class Info{
        int size;
        int searchSize;
        int max;
        int min;
        public Info(int size,int searchSize,int max,int min){
            this.size = size;
            this.searchSize = searchSize;
            this.max = max;
            this.min = min;
        }
    }

    public static Info process(Node node){
        if (node == null){
            return null;
        }

        Info left = process(node.left);
        Info right = process(node.right);

        int size = 1;
        int max = node.value;
        int min = node.value;


        int p1 = -1;
        int p2 = -1;
        int p3 = -1;

        if (left != null){
           max = Math.max(left.max,max);
           min = Math.min(left.min,min);
           size += left.size;
           p1 = left.searchSize;
        }
        if (right != null){
            max = Math.max(right.max,max);
            min = Math.min(right.min,min);
            size += right.size;
            p2 = right.searchSize;
        }

        boolean leftIsSearch = left == null ? true : (left.searchSize == left.size);
        boolean rightIsSearch = right == null ? true : (right.searchSize == right.size);

        if (rightIsSearch && leftIsSearch){
            boolean leftMaxLessNode = left == null ? true : (left.max < node.value);
            boolean ringMinMoreNode = right == null ? true : (right.min > node.value);
            if (leftMaxLessNode && ringMinMoreNode){
                int leftSize = left == null ? 0 : left.size;
                int rightSize = right == null ? 0 : right.size;
                p3 = leftSize + rightSize + 1;
            }
        }

        return  new Info(size,Math.max(p1,Math.max(p2,p3)),max,min);
    }

二叉树树形dp题目通用思路总结:

1.假设以X节点为头,假设可以向X左树和右树要任何信息。

2.在上一步假设下,讨论以头节点为X的树,得到答案的可能性(最重要)

3.列出所有的可能性后,确定到底需要向左树和右树要什么样的信息

4.把左树信息和右树信息求全集,就是任何一课树都要返回的信息S

5.递归函数返回S,每一颗子树都这么要求

6.编写代码,在代码中考虑如何把左树的信息和右树的信息整合出整数的信息。

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值