【算法笔记】有人看海,有人被爱,有人做不出 leetcode 第一题(二叉树专题 Part 8)

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述


Day 18 二叉树


修剪二叉搜索树


题目链接:669. 修剪二叉搜索树 - 力扣(LeetCode)

文章讲解: 代码随想录

视频讲解: 你修剪的方式不对,我来给你纠正一下!| LeetCode:669. 修剪二叉搜索树

题目建议:这道题目比较难,比 添加增加和删除节点难的多,建议先看视频理解。


image-20250516094354159image-20250516094407468image-20250516094529209image-20250516094541870

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode trimBST(TreeNode root, int low, int high) {
        
    }
}

题目解析


关键思路

  1. 利用BST有序特性进行修剪
  2. 递归处理时:
    • 当前节点值小于low时,保留右子树
    • 当前节点值大于high时,保留左子树
    • 当前节点在范围内时,递归修剪左右子树

递归解法

class Solution {
    public TreeNode trimBST(TreeNode root, int low, int high) {
        if (root == null) return null;
        
        // 当前节点值小于low,保留右子树
        if (root.val < low) {
            return trimBST(root.right, low, high);
        }
        
        // 当前节点值大于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;
    }
}

迭代解法

class Solution {
    public TreeNode trimBST(TreeNode root, int low, int high) {
        if (root == null) return null;
        
        // 1. 先处理根节点,使其落在[low, high]范围内
        
        // 处理逻辑, 如果 root.val 不在该范围内, 并且 root != null, 就继续调整
        // 如果 root 已经在范围内, 或者 root 为空, 则结束调整
        // err: root != null 要放在前面
        while (root != null && (root.val < low || root.val > high)) {
            root = root.val < low ? root.right : root.left;
        }
        
        TreeNode curr = root;
        
        // 2. 修剪左子树(移除小于low的节点)
        while (curr != null) {
            // err: 要使用循环处理左子树, 根节点符合题意, 左节点一定小于 high, 只需考虑左节点是否小于 low
            while (curr.left != null && curr.left.val < low) {
                curr.left = curr.left.right;
            }
            curr = curr.left;
        }
        
        curr = root;
        
        // 3. 修剪右子树(移除大于high的节点)
        while (curr != null) {
            // err: 要使用循环处理右子树, 根节点符合题意, 右节点一定大于 low, 只需考虑右节点是否小于 high
            while (curr.right != null && curr.right.val > high) {
                curr.right = curr.right.left;
            }
            curr = curr.right;
        }
        
        return root;
    }
}

关键点说明

  1. 递归解法特点:

    • 代码简洁直观
    • 通过返回值自动完成子树连接
    • 时间复杂度O(n)
  2. 迭代解法特点:

    • 分三步处理:根节点定位、左子树修剪、右子树修剪
    • 不需要使用栈,直接修改指针
    • 时间复杂度O(n)
  3. 共同注意事项:

    • 保持 BST 的有序性
    • 不需要实际删除节点,只需修改指针
    • 处理边界条件(root == null

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


题目链接:108. 将有序数组转换为二叉搜索树 - 力扣(LeetCode)

文章讲解:代码随想录

视频讲解:构造平衡二叉搜索树!| LeetCode:108.将有序数组转换为二叉搜索树

题目建议:本题就简单一些,可以尝试先自己做做。


image-20250516094747954image-20250516094803343image-20250516094818306image-20250516094831231image-20250516094908621

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     int val;
 *     TreeNode left;
 *     TreeNode right;
 *     TreeNode() {}
 *     TreeNode(int val) { this.val = val; }
 *     TreeNode(int val, TreeNode left, TreeNode right) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        
    }
}

题目解析


做这道题目之前大家可以了解一下这几道:


关键思路

  1. 每次取数组中间元素作为根节点

那么为问题来了,如果数组长度为偶数,中间节点有两个,取哪一个?

取哪一个都可以,只不过构成了不同的平衡二叉搜索树

例如:输入:[-10,-3,0,5,9]

如下两棵树,都是这个数组的平衡二叉搜索树:

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

如果要分割的数组长度为偶数的时候,中间元素为两个,是取左边元素 就是树1,取右边元素就是树2。

这也是题目中强调答案不是唯一的原因。 理解这一点,这道题目算是理解到位了

  1. 递归构建左子树和右子树

  2. 自然形成高度平衡的BST(左右子树高度差不超过1)


递归解法

class Solution {
    int[] nums;
    public TreeNode sortedArrayToBST(int[] nums1) {
        nums = nums1;
        return dfs(0, nums.length-1);
    }

    private TreeNode dfs(int start, int end){
        if(start > end){
            return null;
        }
        
        // 取数组中间元素作为根节点, 并防止整数溢出
        int midIndex = start + (end - start)/2;
        TreeNode root = new TreeNode(nums[midIndex]);

        // 递归构建该节点的左右子树
        root.left = dfs(start, midIndex-1);
        root.right = dfs(midIndex+1, end);

        // 把处理好的根节点向上返回
        return root;
    }
}

迭代解法

class Solution {
    public TreeNode sortedArrayToBST(int[] nums) {
        if (nums.length == 0) return null;
        
        TreeNode root = new TreeNode(0);
        Queue<TreeNode> nodeQueue = new LinkedList<>();
        Queue<Integer> leftQueue = new LinkedList<>();
        Queue<Integer> rightQueue = new LinkedList<>();
        
        nodeQueue.offer(root);
        leftQueue.offer(0);
        rightQueue.offer(nums.length - 1);
        
        while (!nodeQueue.isEmpty()) {
            TreeNode curr = nodeQueue.poll();
            int left = leftQueue.poll();
            int right = rightQueue.poll();
            int mid = left + (right - left) / 2;
            
            curr.val = nums[mid];
            
            if (left <= mid - 1) {
                curr.left = new TreeNode(0);
                nodeQueue.offer(curr.left);
                leftQueue.offer(left);
                rightQueue.offer(mid - 1);
            }
            
            if (right >= mid + 1) {
                curr.right = new TreeNode(0);
                nodeQueue.offer(curr.right);
                leftQueue.offer(mid + 1);
                rightQueue.offer(right);
            }
        }
        
        return root;
    }
}

关键点说明

  1. 递归解法特点:

    • 代码简洁直观
    • 时间复杂度O(n)
    • 空间复杂度O(logn)(递归栈)
  2. 迭代解法特点:

    • 使用队列模拟递归过程
    • 避免递归栈溢出风险
    • 同样保证平衡性
  3. 共同注意事项:

    • 中间位置计算防止整数溢出
    • 处理空数组情况
    • 保持 BST 的有序性和平衡性

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


题目链接:538. 把二叉搜索树转换为累加树 - 力扣(LeetCode)

文章讲解:代码随想录

视频讲解:普大喜奔!二叉树章节已全部更完啦!| LeetCode:538.把二叉搜索树转换为累加树

题目建议:本题也不难,在 求二叉搜索树的最小绝对差众数 那两道题目 都讲过了 双指针法,思路是一样的。


image-20250516095108340

1038. 从二叉搜索树到更大和树 - 力扣(LeetCode)

image-20250516095141564

image-20250516095154800


题目解析


关键思路

关键点:将二叉搜索树转换为累加树,可以类比为对有序数组从后向前累加。因为二叉搜索树的中序遍历是有序的,所以需要进行反中序遍历(右-中-左),并在遍历过程中累加节点值

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

本题依然需要一个pre指针记录当前遍历节点cur的前一个节点,这样才方便做累加。

  1. 利用 BST 的中序遍历特性(有序性)
  2. 反向中序遍历(右-中-左)实现从大到小访问节点
  3. 累加前面所有节点的值到当前节点

递归解法

class Solution {
    int pre = 0;
    public TreeNode convertBST(TreeNode root) {
        return dfs(root);
    }

    private TreeNode dfs(TreeNode root){
        if(root == null){
            return null;
        }

        dfs(root.right);
        
        root.val += pre;
        pre = root.val;
        
        dfs(root.left);
        
        return root;
    }
}

迭代解法

class Solution {
    public TreeNode convertBST(TreeNode root) {
        if(root == null){
            return null;
        }

        Stack<TreeNode> stack = new Stack<>();
        TreeNode cur = root;
        int pre = 0;

        // stack.push(cur); 
        // err: 循环开始前将cur压入栈中,这会导致栈中多出一个不必要的节点。

        while(cur != null || !stack.isEmpty()){
            if(cur != null){
                // 先遍历右子树
                stack.push(cur);
                cur = cur.right;
            }else{
                // 处理当前节点
                cur = stack.pop();
                cur.val += pre;
                pre = cur.val;

                // 遍历左子树
                cur = cur.left;
            }
        }
        return root;
    }
}

关键点说明

  1. 反向中序遍历从最大的节点开始处理,依次累加
  2. 累加逻辑:
    • 维护一个全局累加变量 pre
    • pre 和 root.val 类似于固定窗口, 处理方向中序遍历的相邻两个节点
    • 每个节点的值更新为 pre += root.val
  3. 时间复杂度:O(n),每个节点访问一次
  4. 空间复杂度:
    • 递归:O(h),h为树高
    • 迭代:O(n),最坏情况下栈空间

二叉树总结篇


好了,二叉树大家就这样刷完了,做一个总结吧:代码随想录

树结构操作与遍历


在这里插入图片描述

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值