[Java]Java中Tree的常见考题

Java中Tree的常见考题

一、树的定义

public static class TreeNode {
        String val;
        TreeNode left;
        TreeNode right;
        TreeNode(String x) {
            val = x;
        }
    }

二、树的建立

1.由lists建立树

根据二叉树的性质:

将一个完全二叉树按照从上到下,从左到右进行编号,对树的编号为1~n。 其编号为 i
的节点:如果满足2*i<=n,则说明编号为i的节点有左孩子,否则没有;
如果满足2*i+1<=n,则说明编号为i的节点有右孩子,否则没有;

推出:

  • 对于数组编号1~n来说:2*i<=n有左孩子;2*i+1<=n有右孩子;
    代码:
	for(int i=1;i<=a.size()/2;i++) {
		if(2*i-1<=a.size()-1)
			nodeList.get(i-1).left = nodeList.get(2*i-1);
		if(2*i<=a.size()-1)
			nodeList.get(i-1).right = nodeList.get(2*i);
	}
  • 对于数组编号0~n-1来说:2*i<=n-1有左孩子;2*i+1<=n-1有右孩子;
    代码:
         for(int i=0;i<a.size()/2;i++) {
             if(2*i<=a.size()-1)
                nodeList.get(i).left = nodeList.get(2*i+1);
             if(2*i+1<=a.size()-1)
                nodeList.get(i).right = nodeList.get(2*i+2);
         }

完整代码:

	/* 根据List创建二叉树 */
	/**
	对于数组编号1~n代码:
         for(int i=1;i<=a.size()/2;i++) {
             if(2*i-1<=a.size()-1)
                nodeList.get(i-1).left = nodeList.get(2*i-1);
             if(2*i<=a.size()-1)
                nodeList.get(i-1).right = nodeList.get(2*i);
         }
     对于数组编号0~n-1代码:
         for(int i=0;i<a.size()/2;i++) {
             if(2*i<=a.size()-1)
                nodeList.get(i).left = nodeList.get(2*i+1);
             if(2*i+1<=a.size()-1)
                nodeList.get(i).right = nodeList.get(2*i+2);
         }*/
    public TreeNode createTree(List<String> lists){
        List<TreeNode> nodeList = new ArrayList<>();
        if(lists.size()==1)
            return new TreeNode(lists.get(0));
        for(int i=0;i<lists.size();i++)
            nodeList.add(new TreeNode(lists.get(i)));
        for(int i=1;i<=lists.size()/2;i++) {
            if(2*i-1<=lists.size()-1)
                nodeList.get(i-1).left = nodeList.get(2*i-1);
            if(2*i<=lists.size()-1)
                nodeList.get(i-1).right = nodeList.get(2*i);
        }
        /*for(int i=0;i<a.size()/2;i++) {
            if(2*i<=a.size()-1)
                nodeList.get(i).left = nodeList.get(2*i+1);
            if(2*i+1<=a.size()-1)
                nodeList.get(i).right = nodeList.get(2*i+2);
        }*/
        return nodeList.get(0);
    }

2.指定父节点建立树

思路:检查当前节点的两个位置的子节点是否有空余,有的话添加即可,没有的话就输出报错信息。

	/* 指定父节点构造树 */
    public void insertNodeToLeft(TreeNode node,String number){
        if(node.left == null){
            node.left = new TreeNode(number);
        }else if(node.right == null){
            node.right = new TreeNode(number);
        }else{
            System.out.println("该节点不可以再插入新结点,因为该节点度数已饱和");
        }
    }

三、树的遍历

1.先序遍历

使用递归进行遍历,以下三个语句的顺序决定遍历顺序。

res.add(String.valueOf(root.val));
preorderHelper(root.left, res);
preorderHelper(root.right, res);

说明:因为在根据lists生成树的时候将值为null的也插入了,所以在遍历的时候就不在遍历值为null的了;

	/* 先序遍历 */
    public List<String> preorderTraversal(TreeNode root) {
        List<String> res = new ArrayList<>();
        preorderHelper(root, res);
        return res;
    }

    private void preorderHelper(TreeNode root, List<String> res) {
        if (root == null) return;
        if(root.val != "null"){
            res.add(String.valueOf(root.val));
        }
        preorderHelper(root.left, res);
        preorderHelper(root.right, res);
    }

2.中序遍历

	/* 中序遍历 */
    public List<String> inorderTraversal(TreeNode root) {
        List<String> res = new ArrayList<>();
        inorderHelper(root, res);
        return res;
    }

    private void inorderHelper(TreeNode root, List<String> res) {
        if (root == null) return;
        inorderHelper(root.left, res);
        if(root.val != "null"){
            res.add(String.valueOf(root.val));
        }
        inorderHelper(root.right, res);
    }

3.后序遍历

	/* 后序遍历 */
    public List<String> postorderTraversal(TreeNode root) {
        List<String> res = new ArrayList<>();
        postorderHelper(root, res);
        return res;
    }

    private void postorderHelper(TreeNode root, List<String> res) {
        if (root == null) return;
        postorderHelper(root.left, res);
        postorderHelper(root.right, res);
        if(root.val != "null"){
            res.add(String.valueOf(root.val));
        }
    }

4.层次遍历

最简单的解法就是递归,首先确认树非空,然后调用递归函数 helper(node, level),参数是当前节点和节点的层次。程序过程如下:
输出列表称为 levels,当前最高层数就是列表的长度 len(levels)。比较访问节点所在的层次 level 和当前最高层次 len(levels) 的大小,如果前者更大就向 levels 添加一个空列表。
将当前节点插入到对应层的列表 levels[level] 中。
递归非空的孩子节点:helper(node.left / node.right, level + 1)
参考:https://leetcode-cn.com/problems/binary-tree-level-order-traversal/solution/er-cha-shu-de-ceng-ci-bian-li-by-leetcode/

	/* 层次遍历 */
    List<List<String>> levels = new ArrayList<>();
    public void helper(TreeNode node, int level) {
        // start the current level
        if (levels.size() == level)
            levels.add(new ArrayList<>());

        // fulfil the current level
        if(!node.val.equals("null")){
            levels.get(level).add(node.val);
        }

        // process child nodes for the next level
        if (node.left != null)
            helper(node.left, level + 1);
        if (node.right != null)
            helper(node.right, level + 1);
    }
    public List<List<String>> levelOrder(TreeNode root) {
        if (root == null) return levels;
        helper(root, 0);
        return levels;
    }

四、二叉树的最大深度和最小深度

递归:https://leetcode-cn.com/problems/maximum-depth-of-binary-tree/solution/er-cha-shu-de-zui-da-shen-du-by-leetcode/

1. 最大深度

	/* 二叉树最大深度 */
    int maxDeath(TreeNode node){
        if(node==null){
            return 0;
        }
        int left = maxDeath(node.left);
        int right = maxDeath(node.right);
        return Math.max(left,right) + 1;
    }

2.最小深度

    /* 二叉树最小深度 */
    int getMinDepth(TreeNode root){
        if(root == null){
            return 0;
        }
        return getMin(root);
    }
    int getMin(TreeNode root){
        if(root == null){
            return Integer.MAX_VALUE;
        }
        if(root.left == null&&root.right == null){
            return 1;
        }
        return Math.min(getMin(root.left),getMin(root.right)) + 1;
    }

五、节点个数

1.节结点个数

	/* 二叉树节点个数 */
    int numOfTreeNode(TreeNode root){
        if(root == null){
            return 0;
        }
        int left = numOfTreeNode(root.left);
        int right = numOfTreeNode(root.right);
        return left + right + 1;
    }

2.第k层结点个数

	/* 二叉树第k层节点个数 */
    int numsOfkLevelTreeNode(TreeNode root,int k){
        if(root == null||k<1){
            return 0;
        }
        if(k==1){
            return 1;
        }
        int numsLeft = numsOfkLevelTreeNode(root.left,k-1);
        int numsRight = numsOfkLevelTreeNode(root.right,k-1);
        return numsLeft + numsRight;
    }

1.叶子结点个数

	/* 二叉树叶子结点个数 */
    int numsOfNoChildNode(TreeNode root){
        if(root == null){
            return 0;
        }
        if(root.left==null&&root.right==null){
            return 1;
        }
        return numsOfNoChildNode(root.left)+numsOfNoChildNode(root.right);
    }

六、判断二叉树是否为平衡二叉树

	/* 判断二叉树是否为平衡二叉树 */
    boolean isBalanced(TreeNode node){
        return maxDeath2(node)!=-1;
    }
    int maxDeath2(TreeNode node){
        if(node == null){
            return 0;
        }
        int left = maxDeath2(node.left);
        int right = maxDeath2(node.right);
        if(left==-1||right==-1||Math.abs(left-right)>1){
            return -1;
        }
        return Math.max(left, right) + 1;
    }

七、二叉树是否相同与二叉树是否互为镜像

 	/*二叉树是否相同*/
    boolean isSameTreeNode(TreeNode t1,TreeNode t2){
        if(t1==null&&t2==null){
            return true;
        }
        else if(t1==null||t2==null){
            return false;
        }
        if(t1.val != t2.val){
            return false;
        }
        boolean left = isSameTreeNode(t1.left,t2.left);
        boolean right = isSameTreeNode(t1.right,t2.right);
        return left&&right;

    }

    /* 二叉树是否互为镜像*/
    boolean isMirror(TreeNode t1,TreeNode t2){
        if(t1==null&&t2==null){
            return true;
        }
        if(t1==null||t2==null){
            return false;
        }
        if(t1.val != t2.val){
            return false;
        }
        return isMirror(t1.left,t2.right)&&isMirror(t1.right,t2.left);

    }

八、两个二叉树最小公共祖先

解决方法:递归
首先在二叉树中搜索给定的节点 p 和 q,然后找到它们的最近共同祖先。我们可以使用普通的树遍历来搜索这两个节点。一旦我们达到所需的节点 p和 q,我们就可以回溯并找到最近的共同祖先。
这种方法非常直观。先深度遍历该树。当你遇到节点 p 或 q 时,返回一些布尔标记。该标志有助于确定是否在任何路径中找到了所需的节点。最不常见的祖先将是两个子树递归都返回真标志的节点。它也可以是一个节点,它本身是p或q中的一个,对于这个节点,子树递归返回一个真标志。
让我们看看基于这个想法的形式算法。
算法:
从根节点开始遍历树。
如果当前节点本身是 p 或 q 中的一个,我们会将变量 mid 标记为 true,并继续搜索左右分支中的另一个节点。
如果左分支或右分支中的任何一个返回 true,则表示在下面找到了两个节点中的一个。
如果在遍历的任何点上,左、右或中三个标志中的任意两个变为 true,这意味着我们找到了节点 p 和 q 的最近公共祖先。

	/* 两个二叉树最小公共祖先*/
    private TreeNode ans = null;
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        recurseTree(root, p, q);
        return ans;
    }
    private boolean recurseTree(TreeNode currentNode, TreeNode p, TreeNode q) {
        // If reached the end of a branch, return false.
        if (currentNode == null) {
            return false;
        }
        // Left Recursion. If left recursion returns true, set left = 1 else 0
        int left = recurseTree(currentNode.left, p, q) ? 1 : 0;
        // Right Recursion
        int right = recurseTree(currentNode.right, p, q) ? 1 : 0;
        // If the current node is one of p or q
        int mid = (currentNode == p || currentNode == q) ? 1 : 0;
        // If any two of the flags left, right or mid become True
        if (mid + left + right >= 2) {
            ans = currentNode;
        }
        // Return true if any one of the three bool values is True.
        return (mid + left + right > 0);
    }

九、查找节点node是否在当前二叉树中

	// 查找节点node是否在当前二叉树中
    boolean findNode(TreeNode root,TreeNode node){
        if(root == null || node == null){
            return false;
        }
        if(root == node){
            return true;
        }
        boolean found = findNode(root.left,node);
        if(!found){
            found = findNode(root.right,node);
        }
        return found;
    }

十、二叉树内两个节点的最长距离

二叉树中两个节点的最长距离可能有两种情况:

  1. 左子树的最大深度+右子树的最大深度为二叉树的最长距离
  2. 左子树或右子树中的最长距离即为二叉树的最长距离
    因此,递归求解即可
    private static class Result{
        int maxDistance;
        int maxDepth;
        public Result() {
        }

        public Result(int maxDistance, int maxDepth) {
            this.maxDistance = maxDistance;
            this.maxDepth = maxDepth;
        }
    }
    int getMaxDistance(TreeNode root){
        return getMaxDistanceResult(root).maxDistance;
    }
    Result getMaxDistanceResult(TreeNode root){
        if(root == null){
            Result empty = new Result(0,-1);
            return empty;
        }
        Result lmd = getMaxDistanceResult(root.left);
        Result rmd = getMaxDistanceResult(root.right);
        Result result = new Result();
        result.maxDepth = Math.max(lmd.maxDepth,rmd.maxDepth) + 1;
        result.maxDistance = Math.max(lmd.maxDepth + rmd.maxDepth,Math.max(lmd.maxDistance,rmd.maxDistance));
        return result;
    }

十一、将有序数组转为二叉搜索树

题目给出的升序数组就是二叉搜索树的中序遍历。
根据中序遍历还原一颗树,根据根节点,就可以递归的生成左右子树。
这里的话怎么知道根节点呢?平衡二叉树,既然要做到平衡,我们只要把根节点选为数组的中点即可。
综上,和之前一样,找到了根节点,然后把数组一分为二,进入递归即可。注意这里的边界情况,包括左边界,不包括右边界。

	/* 将有序数组转为二叉搜索树*/
    public TreeNode sortedArrayToBST(List<String> nums) {
        return sortedArrayToBST(nums, 0, nums.size());
    }
    private TreeNode sortedArrayToBST(List<String> nums, int start, int end) {
        if (start == end) {
            return null;
        }
        int mid = (start + end)/2;
        TreeNode root = new TreeNode(nums.get(mid));
        root.left = sortedArrayToBST(nums, start, mid);
        root.right = sortedArrayToBST(nums, mid + 1, end);
        return root;
    }

所有源码:https://github.com/zhangzhishun/java

发布了84 篇原创文章 · 获赞 23 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_36254699/article/details/101114203