LeetCode刷题之二叉树总结

在这里插入图片描述

在这里插入图片描述

二叉树的遍历

	 1
	/ \
   2   3
  / \   \
 4   5   6
  • 层次遍历顺序:[1 2 3 4 5 6]
  • 前序遍历顺序:[1 2 4 5 3 6] 根左右
  • 中序遍历顺序:[4 2 5 1 3 6] 左根右
  • 后序遍历顺序:[4 5 2 6 3 1] 左右根

层次遍历使⽤ BFS 实现,利⽤的就是 BFS ⼀层⼀层遍历的特性;⽽前序、中序、后序遍历利⽤了 DFS 实现。前序、中序、后序遍只是在对节点访问的顺序有⼀点不同,其它都相同。

递归实现

前序

void dfs(TreeNode root) {
	visit(root);
	dfs(root.left);
	dfs(root.right);
}

中序

void dfs(TreeNode root){
    dfs(root.left);
    visit(root);
    dfs(root.right);
}

后序

void dfs(TreeNode root){
    dfs(root.left);
    dfs(root.right);
    visit(root);
}

迭代实现

前序

class Solution {
    
    //借助栈,前:根左右
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> ans = new ArrayList<>();
        Stack<TreeNode> stack = new Stack<>();
        stack.push(root);
        while(!stack.isEmpty()){
            TreeNode node = stack.pop();
            //栈是先进后出,前序先访问左边后访问右边,所以先放右边,这样才能保证左子树先访问到
            if(node == null) continue;
            ans.add(node.val);
            stack.push(node.right);
            stack.push(node.left);
        }
        return ans;
    }
}

中序

对于平衡二叉树来说,中序遍历得到的结果就是排序好的

class Solution {

    //中序遍历,左根右
    public List<Integer> inorderTraversal(TreeNode root) {
        List<Integer> ans = new ArrayList<>();
        if(root == null) return ans;
        Stack<TreeNode> stack = new Stack<>();
        //一直拿到最左边的节点
        TreeNode cur = root;
        while(cur != null || !stack.isEmpty()){
            while(cur != null){
                stack.push(cur);
                cur = cur.left;
            }
            //直到拿到最左边的节点
            TreeNode node = stack.pop();
            ans.add(node.val);
            //在组找当前节点的又节点
            cur = node.right;
        }
        return ans;
    }
}

后序

前序遍历为 root -> left -> right,后序遍历为 left -> right -> root。可以修改前序遍历成为 root -> right -> left,那么这个顺序就和后序遍历正好相反。

class Solution {
    //参照前序遍历的 根左右,改成:根右左,最后翻转:左根右就是后续遍历
    public List<Integer> postorderTraversal(TreeNode root) {
        List<Integer> ans = new ArrayList<>();
        if(root == null) return ans;
        Stack<TreeNode>  stack = new Stack<>();
        stack.push(root);
        while(!stack.isEmpty()){
            TreeNode node = stack.pop();
            if(node == null) continue;
            //根右左
            ans.add(node.val);
            stack.push(node.left);
            stack.push(node.right);
        } 
        Collections.reverse(ans);
        return ans;
    }
}

101. 对称二叉树

请添加图片描述

递归版本:

class Solution {
    public boolean isSymmetric(TreeNode root) {
        return isMetirc(root.left,root.right);
    }

    public boolean isMetirc(TreeNode a,TreeNode b){
        if(a == null && b == null) return true;
        if(a == null || b == null) return false;
        if(a.val != b.val) return false;
        return isMetirc(a.left, b.right) && isMetirc(a.right, b.left);
    }
}

迭代版本:

树的层序遍历,都是借用队列来实现。

class Solution {
    public boolean isSymmetric(TreeNode root) {
        return check(root, root);
    }

    public boolean check(TreeNode u, TreeNode v) {
        Queue<TreeNode> q = new LinkedList<TreeNode>();
        q.offer(u);
        q.offer(v);
        while (!q.isEmpty()) {
            u = q.poll();
            v = q.poll();
            if (u == null && v == null) {
                continue;
            }
            if ((u == null || v == null) || (u.val != v.val)) {
                return false;
            }

            q.offer(u.left);
            q.offer(v.right);

            q.offer(u.right);
            q.offer(v.left);
        }
        return true;
    }
}

104. 二叉树的最大深度

请添加图片描述

class Solution {

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

108. 将有序数组转换为二叉搜索树

请添加图片描述
请添加图片描述

class Solution {

    public TreeNode sortedArrayToBST(int[] nums) {
        return toBST(nums,0,nums.length - 1);
    }

    public TreeNode toBST(int[] nums,int left,int right){
        if(left > right) return null;
        int mid = (left + right) / 2;
        TreeNode root = new TreeNode(nums[mid]);
        root.left = toBST(nums,left,mid - 1);
        root.right = toBST(nums,mid + 1,right);
        return root;
    }
}

109. 有序链表转换二叉搜索树

请添加图片描述
与有序数组转换为二叉搜索树相同:
难点:

  • 通过快慢指针找到链表的中点的前一个节点。
  • 找到中点后断链,将前部分,mid,剩下的部分分别作为二叉树的左子树和右子树
class Solution {
    //类似于排序数组的操作,排序数组需要找到终点,那么排序链表也需要每次找到终点
    public TreeNode sortedListToBST(ListNode head) {
        if(head == null) return null;
        if(head.next == null) return new TreeNode(head.val);
        ListNode preMid = preMid(head);
        //断连,mid 为根节点
        ListNode mid = preMid.next;
        preMid.next = null;
        TreeNode root = new TreeNode(mid.val);
        root.left = sortedListToBST(head);
        root.right = sortedListToBST(mid.next);
        return root;
    }

    //找到中点的前一个节点
    public ListNode preMid(ListNode head){
        if(head == null) return head;
        ListNode slow = head,fast = head.next,pre = head;
        while(fast != null && fast.next != null){
            pre = slow;
            slow = slow.next;
            fast = fast.next.next;
        }
        return pre;
    }
}

110. 平衡二叉树

请添加图片描述

代码:

class Solution {

    boolean result = true;

    public boolean isBalanced(TreeNode root) {
        dep(root);
        return result;
    }
    
    public int dep(TreeNode node){
        if(node == null) return 0;
        int left = dep(node.left);
        int right = dep(node.right);
        if(Math.abs(left - right) > 1) result = false;
        return Math.max(left,right) + 1;
    }
}

111. 二叉树的最小深度

请添加图片描述

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

112. 路径总和

请添加图片描述

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

144. 二叉树的前序遍历

请添加图片描述

前序遍历:根左右

1.递归
class Solution {
    //递归方法,前:根左右
    public List<Integer> preorderTraversal(TreeNode root) {
        List<Integer> ans = new ArrayList<>();
        dfs(root,ans);
        return ans;
    }
    public void dfs(TreeNode root,List<Integer> ans){
        if(root == null) return;
        ans.add(root.val);
        dfs(root.left,ans);
        dfs(root.right,ans);
    }
}

226. 翻转二叉树

请添加图片描述

class Solution {
    //就跟swap函数交换一样
    public TreeNode invertTree(TreeNode root) {
        if(root == null) return null;
        TreeNode left = root.left;
        root.left = invertTree(root.right);
        root.right = invertTree(left);
        return root;
    }
}

230. 二叉搜索树中第K小的元素

请添加图片描述
二叉搜索树的中序遍历为有序数组。

class Solution {
    private int cnt = 0;
    private int ans = 0;

    //中序遍历
    public int kthSmallest(TreeNode root, int k) {
        dfs(root,k);
        return ans;
    }

    public void dfs(TreeNode root,int k){
        if(root == null) return;
        dfs(root.left,k);
        cnt++;
        if(cnt == k){
            ans = root.val;
            return;
        }
        dfs(root.right,k);
    }
}

337. 打家劫舍 III

请添加图片描述

方法一:递归

分情况:偷 root 节点 或 不偷 root节点,leetCode 提交超时,然后采用HashMap来做备忘录,还是超时。

class Solution {
    //递归,分两种情况:偷root节点,不偷root节点
    public int rob(TreeNode root) {
        if(root == null) return 0;
        //偷root节点
        int rootVal = root.val;
        if(root.left != null) rootVal += rob(root.left.left) + rob(root.left.right);
        if(root.right != null) rootVal += rob(root.right.left)  + rob(root.right.right);
        //不偷root节点
        int noRoot = rob(root.left) + rob(root.right);
        return Math.max(rootVal,noRoot);
    }
}

方法二:递归借助 HashMap

请添加图片描述

class Solution {
    //递归,分两种情况:偷root节点,不偷root节点,超时
    public int rob(TreeNode root) {
        Map<TreeNode,Integer> map = new HashMap<>();
        return rob(root,map);
    }
    public int rob(TreeNode root,Map<TreeNode,Integer> map){
        if(root == null) return 0;
        //借助备忘录
        if(map.containsKey(root)) return map.get(root);
        //偷root节点
        int rootVal = root.val;
        if(root.left != null) rootVal += rob(root.left.left) + rob(root.left.right);
        if(root.right != null) rootVal += rob(root.right.left)  + rob(root.right.right);
        //不偷root节点
        int noRoot = rob(root.left) + rob(root.right);
        int max = Math.max(rootVal,noRoot);
        map.put(root,max);
        return max;
    }
}

方法三:

每个节点可选择偷或者不偷两种状态,根据题目意思,相连节点不能一起偷

1.当前节点选择偷时,那么两个孩子节点就不能选择偷了
2.当前节点选择不偷时,两个孩子节点只需要拿最多的钱出来就行(两个孩子节点偷不偷没关系)
3.我们使用一个大小为 2 的数组来表示 int[] res = new int[2] 0 代表不偷,1 代表偷
任何一个节点能偷到的最大钱的状态可以定义为

当前节点选择不偷:当前节点能偷到的最大钱数 = 左孩子能偷到的钱 + 右孩子能偷到的钱
当前节点选择偷:当前节点能偷到的最大钱数 = 左孩子选择自己不偷时能得到的钱 + 右孩子选择不偷时能得到的钱 + 当前节点的钱数
表示为公式如下

root[0] = Math.max(rob(root.left)[0], rob(root.left)[1]) + Math.max(rob(root.right)[0], rob(root.right)[1])
root[1] = rob(root.left)[0] + rob(root.right)[0] + root.val;
class Solution {
    public int rob(TreeNode root) {
        int[] ans = dfs(root);
        return Math.max(ans[0],ans[1]);
    }

    // int[] = new int[2] 数组中 0 表示该点不偷,1 表示该点偷
    public int[] dfs(TreeNode root){
        if(root == null) return new int[2];
        int[] result = new int[2];
        int[] left = dfs(root.left);
        int[] right = dfs(root.right);
        //情况1,不偷 root节点,则可以偷 left 节点,改节点中取较大值,即可偷left,也可以不偷left,选择其中较大的值
        result[0] = Math.max(left[0],left[1]) + Math.max(right[0],right[1]);
        //情况2,偷了 root 节点,则left和right都不能偷
        result[1] = left[0] + right[0] + root.val;
        return result;
    }
}

404. 左叶子之和

请添加图片描述

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

    //判定该节点是否是叶子节点
    public boolean isLeaf(TreeNode node){
        if(node == null) return false;
        return node.left == null && node.right == null;
    }
}

437. 路径总和 III

请添加图片描述

因为题目要求是从任何节点往下遍历都可以,所以分两种情况:

  • 包括当前 root 节点的递归
  • 不包括当前节点的,只从左右子树中查找
class Solution {

    //分三种情况递归包括root节点,不包括root节点
    public int pathSum(TreeNode root, int targetSum) {
        if(root == null) return 0;
        return pathWithRootSum(root,targetSum) + pathSum(root.left,targetSum) + pathSum(root.right,targetSum);
    }

    public int pathWithRootSum(TreeNode root,int targetSum){
        if(root == null) return 0;
        int ret = 0;
        targetSum -= root.val;
        if(targetSum == 0) ret++;
        //查找左右子树
        ret += pathWithRootSum(root.left,targetSum) + pathWithRootSum(root.right,targetSum);
        return ret; 
    }
}

513. 找树左下角的值

请添加图片描述请添加图片描述

树的层次遍历,广度遍历

思路:需要找到最底层的最左边的节点,那就通过Queue来实现层次遍历。

注意我们需要找到的是当前层的最左边的节点,基于队列的先进先出的思想。需要先放右节点,在放左节点,这样才能找到最后一个左叶子结点。

class Solution {

    /**
     * 深度优先搜索,也是借助队列,一直找到最底层坐标的节点
     * 前:根左右
     * 中:左根右
     * 后:左右跟
     */
    public int findBottomLeftValue(TreeNode root) {
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while(!queue.isEmpty()){
            root = queue.poll();
            //先添加右边节点,在添加左的节点,队列中先添加的先移出
            if(root.right != null) queue.add(root.right);
            if(root.left != null) queue.add(root.left);
        }
        return root.val;
    }
}

538. 把二叉搜索树转换为累加树

请添加图片描述

class Solution {

    int sum = 0;
    //中序遍历有序,递增(左根右)--》那现在反向遍历:右根左
    public TreeNode convertBST(TreeNode root) {
        dfs(root);
        return root;
    }

    public void dfs(TreeNode root){
        if(root == null) return;
        dfs(root.right);
        sum += root.val;
        root.val = sum;
        dfs(root.left);
    }
}

543. 二叉树的直径

请添加图片描述

class Solution {

    private int max;

    //注意是路径总和,不是节点总和
    public int diameterOfBinaryTree(TreeNode root) {
        depth(root);
        return max;
    }

    public int depth(TreeNode root){
        if(root == null) return 0;
        int left = depth(root.left);
        int right = depth(root.right);
        max = Math.max(max,left + right);
        //选择较长的路劲返回
        return Math.max(left,right) + 1;
    }
}

572. 另一棵树的子树

请添加图片描述

class Solution {

    public boolean isSubtree(TreeNode root, TreeNode subRoot) {
        if(root == null) return false;
        return isSame(root,subRoot) || isSubtree(root.left,subRoot) || isSubtree(root.right,subRoot);
    }

    public boolean isSame(TreeNode a,TreeNode b){
        if(a == null && b == null) return true;
        if(a == null || b == null) return false;
        if(a.val != b.val) return false;
        return isSame(a.left,b.left) && isSame(a.right,b.right);
    }
}

617. 合并二叉树

请添加图片描述

方法一:以左子树为合并后的树

class Solution {
    //方法一:直接以左边的树为合并后的树
    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        if(root1 == null) return root2;
        if(root2 == null) return root1;
        root1.val += root2.val;
        root1.left = mergeTrees(root1.left, root2.left);
        root1.right = mergeTrees(root1.right, root2.right);
        return root1;
    }
}

方法二:新建一个二叉树

class Solution {
    //方法二:新建一个二叉树
    public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {
        if(root1 == null) return root2;
        if(root2 == null) return root1;
        TreeNode root = new TreeNode(root1.val + root2.val) ;
        root.left = mergeTrees(root1.left, root2.left);
        root.right = mergeTrees(root1.right, root2.right);
        return root;
    }
}

637. 二叉树的层平均值

请添加图片描述

1.广度优先搜索

借助队列来实现二叉树的层次遍历,每一层中,当前queue的大小就是当前层的节点的数量。

class Solution {
    //借助队列来实现层次遍历,当前队列的 size 就是这一层节点的数量
    public List<Double> averageOfLevels(TreeNode root) {
        List<Double> ans = new ArrayList<>();
        if(root == null) return ans;
        Queue<TreeNode> queue = new LinkedList<>();
        queue.add(root);
        while(!queue.isEmpty()){
            int size = queue.size();
            double sum = 0;
            for(int i = 0; i < size; i++){
                TreeNode node = queue.poll();
                sum += node.val;
                if(node.left != null) queue.add(node.left);
                if(node.right != null) queue.add(node.right);
            }
            ans.add(sum / size);
        }
        return ans;
    }
}

653. 两数之和 IV - 输入 BST

请添加图片描述

class Solution {

    //先中序遍历,后双指针,或者借助HashSet
    public boolean findTarget(TreeNode root, int k) {
        List<Integer> num = new ArrayList<>();
        dfs(root,num);
        int i = 0,j = num.size() - 1;
        while(i < j){
            int sum = num.get(i) + num.get(j);
            if(sum == k) return  true;
            if(sum < k) i++;
            else j--;
        }
        return false;
    }

    public void dfs(TreeNode root,List<Integer> list){
        if(root == null) return;
        dfs(root.left,list);
        list.add(root.val);
        dfs(root.right,list);
    } 
}

669. 修剪二叉搜索树

请添加图片描述

class Solution {

    //二叉搜索树的左子树比跟节点小,右子树比根节点大
    public TreeNode trimBST(TreeNode root, int low, int high) {
        if(root == null) return null;
        //情况1:low还小,则只能往右子树找了
        if(root.val < low) return trimBST(root.right,low,high);
        //情况2:hight还大,则只能往左子树裁找了
        if(root.val > high) return trimBST(root.left,low,high);
        //情况3:在区间内,直接递归
        root.left = trimBST(root.left,low,high);
        root.right = trimBST(root.right,low,high);
        return root;
    }
}

671. 二叉树中第二小的节点

请添加图片描述

方法一:递归

class Solution {

    public int findSecondMinimumValue(TreeNode root) {
        if(root == null) return -1;
        if(root.left == null && root.right == null) return -1;
        int leftValue = root.left.val;
        int rightValue = root.right.val;
        //当前的值相等,那我们的继续往下找,因为root节点的值是等于子节点中的某个节点的值
        if(root.val == leftValue) leftValue = findSecondMinimumValue(root.left);
        if(root.val == rightValue) rightValue = findSecondMinimumValue(root.right);
        //如果找到了,则表明结果不为 - 1
        if(leftValue != -1 && rightValue != -1) return Math.min(leftValue,rightValue);
        if(leftValue != -1) return leftValue;
        return rightValue;
    }
}

方法二:

我们可以使用深度优先搜索的方法对二叉树进行遍历。

假设当前遍历到的节点为 node,如果 node 的值严格大于 rootvalue,那么我们就可以用 node 的值来更新答案ans。

当我们遍历完整棵二叉树后,即可返回ans。

​根据题目要求,如果第二小的值不存在的话,输出 −1,那么我们可以将 ans 的初始值置为 −1。在遍历的过程中,如果当前节点的值严格大于 rootvalue 的节点时,那么只要ans 的值为 −1 或者当前节点的值严格小于ans,我们就需要对 ans 进行更新。

​ 此外,如果当前节点的值大于等于ans,那么根据「思路」部分,以当前节点为根的子树中所有节点的值都大于等于 ans,我们就直接回溯,无需对该子树进行遍历。这样做可以省去不必要的遍历过程。

class Solution {

    private int ans = -1;//记录结果
    private int rootValue;

    public int findSecondMinimumValue(TreeNode root) {
        if(root == null) return -1;
        rootValue = root.val;
        dfs(root);
        return ans;
    }

    public void dfs(TreeNode root){
        if(root == null) return;
        //找到严格意义上大于 root节点的值
        if(ans != -1 && root.val >= ans) return;
        if(root.val > rootValue){
            ans = root.val;
        }
        dfs(root.left);
        dfs(root.right);
    }
}

687. 最长同值路径

请添加图片描述

class Solution {
    
    private int maxLength = 0;

    public int longestUnivaluePath(TreeNode root) {
        dfs(root);
        return maxLength;
    }

    public int dfs(TreeNode head){
        if(head == null) return 0;
        int left = dfs(head.left);
        int right = dfs(head.right);
        //与根节点的值相同
        int leftPath = head.left != null && head.left.val == head.val ? left + 1: 0;
        int rightPath = head.right != null && head.right.val == head.val ? right + 1: 0;
        maxLength = Math.max(maxLength,leftPath + rightPath);
        return Math.max(leftPath,rightPath);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值