【Leetcode之树】

9 篇文章 0 订阅


前言

题目来源


一、递归

本部分题型大部分使用递归思想解决!!

1、树的高度

给定一个二叉树,找出其最大深度。
二叉树的深度为根节点到最远叶子节点的最长路径上的节点数。

class Solution {
    public int maxDepth(TreeNode root) {
    if (root == null) return 0;
    return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1;
    }
}

2、平衡树

一棵高度平衡二叉树定义为:一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。
在这里插入图片描述
在这里插入图片描述

class Solution {
    public boolean isBalanced(TreeNode root) {
        if(root==null) return true;
        boolean isbalan=Math.abs(depth(root.left)-depth(root.right))<=1 ? true : false;
        return isBalanced(root.left) && isBalanced(root.right) && isbalan;

    }
    public int depth(TreeNode p){
        if(p==null) return 0;
        return Math.max(depth(p.left),depth(p.right))+1;
    }
}

3、两节点的最长路径

给定一棵二叉树,求出两节点的最长路径。这条路径可能穿过也可能不穿过根结点。
在这里插入图片描述

class Solution {
    public int res=0;   //注意这种全局变量的作用!!!
    public int diameterOfBinaryTree(TreeNode root) {
        depth(root);
        return res;
    }
    public int depth(TreeNode node){
        if(node==null) return 0;
        int left=depth(node.left);
        int right=depth(node.right);
        res=Math.max(res,left+right);
        return Math.max(left,right)+1;
    }
}

但是使用局部变量传入时就不太对!!

class Solution {
    public int diameterOfBinaryTree(TreeNode root) {
        int res=0;      //它作为参数传入时是不对的!!!
        depth(root,res);
        return res; 
    }
    public int depth(TreeNode node,int res){
        if(node==null) return 0;
        int left=depth(node.left,res);
        int right=depth(node.right,res);
        res=Math.max(res,left+right);
        return Math.max(left,right)+1;
    }
}

4、翻转树

在这里插入图片描述
在这里插入图片描述

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if(root==null) return root;
        TreeNode left=root.left;
        TreeNode right=root.right;
        invertTree(left);
        invertTree(right);
        root.right=left;
        root.left=right;
        return root;
    }
}

5、归并两棵树

合并的规则是:如果两个节点重叠,那么将这两个节点的值相加作为合并后节点的新值;否则,不为 null 的节点将直接作为新二叉树的节点。
在这里插入图片描述

class Solution {
    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        if(root1==null && root2==null) return null;
        else if(root1==null) return root2;
        else if(root2==null) return root1;
        else {
            TreeNode root=new TreeNode(root1.val+ root2.val); //需要时新建树
            root.left=mergeTrees(root1.left,root2.left);
            root.right=mergeTrees(root1.right,root2.right);
            return root;
        }
    }
}

6、判断路径和是否等于一个数

给你二叉树的根节点 root 和一个表示目标和的整数 targetSum 。判断该树中是否存在根节点到叶子节点 的路径,这条路径上所有节点值相加等于目标和 targetSum 。如果存在,返回 true ;否则,返回 false 。
在这里插入图片描述
在这里插入图片描述

class Solution {
    public boolean hasPathSum(TreeNode root, int targetSum) {
        if(root==null) return false;
        targetSum-=root.val;
        if(targetSum==0 && root.left==null && root.right==null) return true;
        return hasPathSum(root.left,targetSum) || hasPathSum(root.right,targetSum);
    }
}

7、计路径和等于一个数的路径数量

路径不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点)。
在这里插入图片描述

class Solution {
    public int pathSum(TreeNode root, int targetSum) {
        if (root == null) {
            return 0;
        }

        int ret = rootSum(root, targetSum);
        ret += pathSum(root.left, targetSum);
        ret += pathSum(root.right, targetSum);
        return ret;
    }

    public int rootSum(TreeNode root, int targetSum) {
        int ret = 0;
        if (root == null) {
            return 0;
        }
        int val = root.val;
        if (val == targetSum) ret++;
        ret += rootSum(root.left, targetSum - val);
        ret += rootSum(root.right, targetSum - val);
        return ret;
    }
}

8、子树

给你两棵二叉树 root 和 subRoot 。检验 root 中是否包含和 subRoot 具有相同结构和节点值的子树。如果存在,返回 true ;否则,返回 false 。
二叉树 tree 的一棵子树包括 tree 的某个节点和这个节点的所有后代节点。tree 也可以看做它自身的一棵子树。
在这里插入图片描述
在这里插入图片描述

class Solution {
    public boolean isSubtree(TreeNode root, TreeNode subRoot) {
        if(root==null && subRoot==null) return true;
        if(root==null) return false;
        return issub(root,subRoot) || isSubtree(root.left,subRoot) || isSubtree(root.right,subRoot);
    }

    public boolean issub(TreeNode root1,TreeNode root2){
        if(root1==null && root2==null) return true;
        else if(root1==null || root2==null) return false;
        if(root1.val!=root2.val) return false;
        return issub(root1.left,root2.left) && issub(root1.right,root2.right);
    }
}

9、对称二叉树

在这里插入图片描述

class Solution {
    public boolean isSymmetric(TreeNode root) {
        if(root==null) return true;
        return issym(root.left,root.right);  //主要是要调用两个树
    }

    public boolean issym(TreeNode root1,TreeNode root2){
        if(root1==null && root2==null) return true;
        else if(root1==null || root2==null) return false;
        if(root1.val!= root2.val) return false;
        return issym(root1.left,root2.right) && issym(root1.right,root2.left);
    }
}

10、最小路径

给定一个二叉树,找出其最小深度。
最小深度是从根节点到最近叶子节点的最短路径上的节点数量。
在这里插入图片描述
在这里插入图片描述

//最小深度
class Solution {
    public int minDepth(TreeNode root) {
        if(root==null) return 0;
        int left=minDepth(root.left);
        int right=minDepth(root.right);
        if(left==0 || right==0) return left+right+1;  //注意这个特殊情况哈!!
        return Math.min(left,right)+1;
    }
}

11、统计左叶子节点的和

在这里插入图片描述

class Solution {
    public int sumOfLeftLeaves(TreeNode root) {
        if (root == null) return 0;
        if (isLeaf(root.left)) return root.left.val + sumOfLeftLeaves(root.right);
        return sumOfLeftLeaves(root.left) + sumOfLeftLeaves(root.right);
    }

    private boolean isLeaf(TreeNode node){
        if (node == null) return false;
        return node.left == null && node.right == null;
    }
}

12、相同节点值的最大路径长度

给定一个二叉树的 root ,返回最长的路径的长度 ,这个路径中的每个节点具有相同值
这条路径可以经过也可以不经过根节点
两个节点之间的路径长度,由它们之间的边数表示。
在这里插入图片描述
在这里插入图片描述

class Solution {
    int res=0;
    public int longestUnivaluePath(TreeNode root) {
        dfs(root);
        return res;
    }
    //这两个方法看起来一样,但是longestUnivaluePath时并没有用到它的返回值,而是一个全局变量res。
    public int dfs(TreeNode node){
        if(node==null) return 0;
        int left=dfs(node.left);
        int right=dfs(node.right);
        int leftnum= (node.left!=null && node.left.val==node.val) ? left+1 : 0;
        int rightnum= (node.right!=null && node.right.val==node.val) ? right+1 : 0;
        res=Math.max(res,leftnum+rightnum);   //
        return Math.max(leftnum,rightnum);  //如果用当前节点node,只能是它得left或right中的一个路径
    }
}

13、间隔遍历

小偷又发现了一个新的可行窃的地区。这个地区只有一个入口,我们称之为 root 。
除了 root 之外,每栋房子有且只有一个“父“房子与之相连。一番侦察之后,聪明的小偷意识到“这个地方的所有房屋的排列类似于一棵二叉树”。 如果两个直接相连的房子在同一天晚上被打劫 ,房屋将自动报警。给定二叉树的 root 。返回 在不触动警报的情况下 ,小偷能够盗取的最高金额 。
在这里插入图片描述
在这里插入图片描述

class Solution {
    HashMap<TreeNode,Integer> hashMap=new HashMap<>(); //使用hashmap的原因是调用rob函数的时候可以直接获取,不用再计算,减少计算时间。
    public int rob(TreeNode root) {
        if(root==null) return 0;
        if(hashMap.containsKey(root)) return hashMap.get(root);
        int num1=root.val;
        if(root.left!=null) num1+=rob(root.left.left)+ rob(root.left.right);
        if(root.right!=null) num1+=rob(root.right.left)+rob(root.right.right);
        int num2=rob(root.left)+rob(root.right);
        int res=Math.max(num1,num2);
        hashMap.put(root,res);
        return res;
    }
}

14、找出二叉树中第二小的节点

给定一个非空特殊的二叉树,每个节点都是正数,并且每个节点的子节点数量只能为 2 或 0。如果一个节点有两个子节点的话,那么该节点的值等于两个子节点中较小的一个
更正式地说,即 root.val = min(root.left.val, root.right.val) 总成立。给出这样的一个二叉树,你需要输出所有节点中的 第二小的值 。如果第二小的值不存在的话,输出 -1 。
在这里插入图片描述
在这里插入图片描述

//TMD有点不好理解
class Solution {
    int ans;
    int rootvalue;
    public int findSecondMinimumValue(TreeNode root) {
        ans = -1;
        rootvalue = root.val;
        dfs(root);
        return ans;
    }
    public void dfs(TreeNode node) {
        if (node == null) {
            return;
        }
        if (ans != -1 && node.val >= ans) {
            return;
        }
        if (node.val > rootvalue) {
            ans = node.val;
        }
        dfs(node.left);
        dfs(node.right);
    }
}
class Solution {
    public int findSecondMinimumValue(TreeNode root) {
        if (root.left == null) {
            return -1;
        }
        int leftValue = root.left.val;
        int rightValue = root.right.val;
        // 等于 root.val 则是最小值,递归获取该子节点的第二小值
        if (leftValue == root.val) {
            leftValue = findSecondMinimumValue(root.left);
        }
        if (rightValue == root.val) {
            rightValue = findSecondMinimumValue(root.right);
        }
        // 为 -1 说明没有第二小值
        if (leftValue == -1) {
            return rightValue;
        }
        if (rightValue == -1) {
            return leftValue;
        }
        // 左右子节点都有第二小值,则取最小的值
        return leftValue < rightValue ? leftValue : rightValue;
    }
}

二、层序遍历

1、一棵树每层节点的平均数

在这里插入图片描述
在这里插入图片描述

class Solution {
    public List<Double> averageOfLevels(TreeNode root) {
        List<Double> res=new ArrayList<>();
        List<TreeNode> path=new LinkedList<>();
        if(root==null) return res;
        path.add(root);
        int size;
        while (!path.isEmpty()){
            size=path.size();
            double num=0,numsize=size;
            while (size>0){
                TreeNode node=path.remove(0);
                num+=node.val;
                if(node.left!=null) path.add(node.left);
                if(node.right!=null) path.add(node.right);
                --size;
            }
            res.add(num/numsize);
        }
        return res;
    }
}

2、得到左下角的节点

给定一个二叉树的 根节点 root,请找出该二叉树的 最底层 最左边 节点的值。
在这里插入图片描述
在这里插入图片描述

//为了拿到最下面那层的最左边那个,层序遍历时从右到左;
class Solution {
    public int findBottomLeftValue(TreeNode root) {
        Queue<TreeNode> queue=new LinkedList<>();  //用的是队列奥
        queue.add(root);
        TreeNode node=null;
        while (!queue.isEmpty()){
            node=queue.poll();
            if(node.right!=null) queue.add(node.right);
            if(node.left!=null) queue.add(node.left);
        }
        return node.val;
    }
}

三、前中后序遍历

注意这里尽量不要使用递归做法哈,学习非递归的思想!!!

在这里插入图片描述

1、前序遍历

前序遍历的顺序为:中左右

class Solution {
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> list=new LinkedList<>();
        if(root==null) return list;
        Stack<TreeNode> stack=new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()){
            TreeNode node=stack.pop();
            list.add(node.val);
            if(node.right!=null) stack.push(node.right);
            if(node.left!=null) stack.push(node.left);
        }
        return list;
    }
}

2、后序遍历

后序遍历的顺序为:左右中

class Solution {
    public List<Integer> postorderTraversal(TreeNode root) {
        LinkedList<Integer> list=new LinkedList<>();
        if(root==null) return list;
        Stack<TreeNode> stack=new Stack<>();
        stack.push(root);
        while (!stack.isEmpty()){
            TreeNode node=stack.pop();
            list.addFirst(node.val);   //注意这里是addFirst奥,先序遍历是:中左右;后序遍历是:左右中;他们都可以使用Stack和List来实现。
            if(node.left!=null) stack.push(node.left);
            if(node.right!=null) stack.push(node.right);
        }
        return list;
    }
}

前序遍历与后续遍历代码思想很相似,主要使用了Stack和LinkedList;

3、中序遍历

中序遍历的顺序为:左中右

class Solution {
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> res = new ArrayList<Integer>();
        if(root==null) return res;
        Stack<TreeNode> stack=new Stack<>();
        while (root!=null){
            stack.push(root);
            root=root.left;
        }
        while (!stack.isEmpty()){
            TreeNode node=stack.pop();
            res.add(node.val);
            if(node.right!=null){
                node=node.right;
                while (node!=null){
                    stack.push(node);
                    node=node.left;
                }
            }
        }
        return res;
    }
}

四、BST

1、修剪二叉树

给你二叉搜索树的根节点 root ,同时给定最小边界low 和最大边界 high。通过修剪二叉搜索树,使得所有节点的值在[low, high]中。修剪树 不应该 改变保留在树中的元素的相对结构 。 可以证明,存在 唯一的答案 。
在这里插入图片描述
在这里插入图片描述

class Solution {
    public TreeNode trimBST(TreeNode root, int low, int high) {
        if(root==null) return root;
        if(root.val<low) return trimBST(root.right,low,high);
        if(root.val>high) return trimBST(root.left,low,high);
        root.left=trimBST(root.left,low,high);
        root.right=trimBST(root.right,low,high);
        return root;
    }
}

2、寻找二叉查找树的第k个元素

可以使用递归的思想:

class Solution {
    public int kthSmallest(TreeNode root, int k) {
        int left=count(root.left);
        if(left==k-1) return root.val;
        else if(left>k-1) return kthSmallest(root.left,k);
        else return kthSmallest(root.right,k-left-1);
    }

    public int count(TreeNode root){
        if(root==null) return 0;
        int left= root.left!=null ? count(root.left) : 0;
        int right= root.right!=null ? count(root.right) : 0;
        return left+right+1;
    }
}

使用中序遍历,遍历到第k个就输出;

class Solution {
    public int kthSmallest(TreeNode root, int k) {
        Stack<TreeNode> stack=new Stack<>();
        while (root!=null){
            stack.push(root);
            root=root.left;
        }
        while (!stack.isEmpty()){
            TreeNode node=stack.pop();
            if(--k==0) return node.val;
            if(node.right!=null){
                node=node.right;
                while (node!=null){
                    stack.push(node);
                    node=node.left;
                }
            }
        }
        return -1;
    }
}

3、把二叉查找树每个节点的值都加上比它大的节点的值

给定二叉搜索树的根节点,该树的节点值各不相同,请将其转换为累加树,使每个节点node的新值等于原树中大于或等于node.val的值之和。
在这里插入图片描述

class Solution {
    private int sum = 0;  //定义一个全局变量sum
    public TreeNode convertBST(TreeNode root) {
        traver(root);
        return root;
    }
    private void traver(TreeNode node) {
        if (node == null) return;
        traver(node.right);
        sum += node.val;
        node.val = sum;
        traver(node.left);
    }
}

4、二叉查找树的最近公共祖先

“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。
在这里插入图片描述

//不可以直接判断if(p.val<root.val && q.val>root.val)就return root是不可以滴哈。先从左右递归如果都没有才是root。
class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(p.val<root.val && q.val<root.val) return lowestCommonAncestor(root.left,p,q);
        if(p.val>root.val && q.val>root.val) return lowestCommonAncestor(root.right,p,q);
        return root;
    }
}

5、二叉树的最近公共祖先

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
在这里插入图片描述
在这里插入图片描述

class Solution {
    public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
        if(root==null || root==p || root==q) return root;
        TreeNode left=lowestCommonAncestor(root.left,p,q);
        TreeNode right=lowestCommonAncestor(root.right,p,q);
        return left==null ? right : (right==null ? left : root);
    }
}

6、从有序数组中构造二叉查找树

在这里插入图片描述
在这里插入图片描述

 //其实是不用考虑高度平衡问题的,因为方法中每次找的都是mid位置,左右高度最多相差1,所以不用特地做高度平衡。
class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        return toBST(nums,0,nums.length-1);
    }
    public TreeNode toBST(int[] nums,int begin,int end){
        if(begin>end) return null;
        int mid=begin+(end-begin)/2;
        TreeNode node=new TreeNode(nums[mid]);
        node.left=toBST(nums,begin,mid-1);
        node.right=toBST(nums,mid+1,end);
        return node;
    }
}

7、根据有序链表构造平衡二叉树

给定一个单链表的头节点 head ,其中的元素按升序排序,将其转换为高度平衡的二叉搜索树。
本题中,一个高度平衡二叉树是指一个二叉树每个节点 的左右两个子树的高度差不超过 1。
在这里插入图片描述
首先确定中间节点,然后递归创建节点,左右建树。

class Solution {
    public TreeNode sortedListToBST(ListNode head) {
        if(head==null) return null;
        if(head.next==null){
            TreeNode node=new TreeNode(head.val);
            return node;
        }
        ListNode premid=getmid(head);  //获取premid
        ListNode mid=premid.next;
        premid.next=null;
        TreeNode node=new TreeNode(mid.val);
        node.left=sortedListToBST(head);
        node.right=sortedListToBST(mid.next);
        return node;
    }
    private ListNode getmid(ListNode head) {
        ListNode slow = head, fast = head.next;
        ListNode pre = head;  //初始是这个而不是new ListNode(0)!!!
        while (fast != null && fast.next != null) {
            pre = slow;
            slow = slow.next;
            fast = fast.next.next;
        }
        return pre;
    }
}

同样的原理,但是注意Java函数中的局部性:

class Solution {
    public TreeNode sortedListToBST(ListNode head) {
        if(head==null) return null;
        if(head.next==null) return new TreeNode(head.val);
        ListNode mid=getmid(head);
        TreeNode node=new TreeNode(mid.val);
        node.left=sortedListToBST(head);
        node.right=sortedListToBST(mid.next);
        return node;
    }

    private ListNode getmid(ListNode head) {
        ListNode slow = head, fast = head.next;
        ListNode pre = head;  //初始是这个而不是new ListNode(0)!!!
        while (fast != null && fast.next != null) {
            pre = slow;
            slow = slow.next;
            fast = fast.next.next;
        }
        pre.next=null;   //在这里将 pre.next=null;不会影响真正的链表;
        return slow;
    }
}

8、在二查找树中寻找两个节点,使它们的和为一个给定值

给定一个二叉搜索树 root 和一个目标结果 k,如果二叉搜索树中存在两个元素且它们的和等于给定的目标结果,则返回 true。
在这里插入图片描述

class Solution {
    Set<Integer> set = new HashSet<Integer>();
    public boolean findTarget(TreeNode root, int k) {
        if (root == null) {
            return false;
        }
        if (set.contains(k - root.val)) {
            return true;
        }
        set.add(root.val);
        return findTarget(root.left, k) || findTarget(root.right, k);
    }
}

9、在二叉查找树中寻找两个节点之差的最小绝对值

给你一个二叉搜索树的根节点 root ,返回 树中任意两不同节点值之间的最小差值 。差值是一个正数,其数值等于两值之差的绝对值。
在这里插入图片描述
在这里插入图片描述

class Solution {
    int res=Integer.MAX_VALUE;  //全局变量
    TreeNode pre=null;   //定义一个pre节点
    public int getMinimumDifference(TreeNode root) {
        inorder(root);
        return res;
    }

    public void inorder(TreeNode node){
        if(node==null) return;
        inorder(node.left);
        if(pre!=null) res=Math.min(res,Math.abs(pre.val-node.val));
        pre=node;
        inorder(node.right);
    }
}

10、二叉搜索树中的众数

给一个含重复值的二叉搜索树(BST)的根节点 root ,找出并返回 BST 中的所有众数(即,出现频率最高的元素)。
在这里插入图片描述

//嘿嘿,虽然效率不是很高但是做出来了,重点是hashmap的排序
class Solution {
    public int[] findMode(TreeNode root) {
        if(root==null) return new int[]{};
        HashMap<Integer,Integer> hashMap=new HashMap<>();
        travers(root,hashMap);
        List<Map.Entry<Integer,Integer>> list=new ArrayList<>(hashMap.entrySet());
        Collections.sort(list, new Comparator<Map.Entry<Integer, Integer>>() {
            @Override
            public int compare(Map.Entry<Integer, Integer> o1, Map.Entry<Integer, Integer> o2) {
                return o2.getValue()-o1.getValue();
            }
        });
        int size=list.size(),i=0;
        for(i=0;i<size;++i){
            if(i>0 && list.get(i).getValue()<list.get(i-1).getValue())
                break;
        }
        int[] res=new int[i];
        for(int j=0;j<i;++j)
            res[j]=list.get(j).getKey();
        return res;
    }

    public void travers(TreeNode root,HashMap<Integer,Integer> hashMap){
        if(root==null) return;
        travers(root.left,hashMap);
        hashMap.put(root.val,hashMap.getOrDefault(root.val,0)+1);
        travers(root.right,hashMap);
    }
}

总结

提示:以上就是关于Leetcode树部分所有题目啦。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值