代码随想录 Day 27 | 【第六章 二叉树part 08】669. 修剪二叉搜索树、108.将有序数组转换为二叉搜索树、538.把二叉搜索树转换为累加树、总结

一、669. 修剪二叉搜索树

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

1. 常见误区及代码

        常见的错误思路是判断节点值是否在区间范围内,如果不在范围内,直接返回null,砍掉了这颗节点及其下方的树。

       然而,需要注意的是,这是一颗二叉搜索树,如果节点值不在范围,那么应该删除的是节点及其左子树,而它的右子树还需要继续遍历,看是否有满足条件的。

2. 正确的代码实现

(1)确定递归函数的参数和返回值:参数是根节点、左区间、右区间,返回的是修建后树的根节点。

(2)确定终止条件:根节点为空,直接返回null。

        如果当前节点的值是小于左区间的,那么它的右子树可能存在满足范围的元素,所以需要在这个节点的右子树去遍历,最后返回新右子树的根节点。

        如果当前节点的值是大于右区间的,那么它的左子树可能存在满足范围的元素,所以需要在这个节点的左子树去遍历,最后返回新左子树的根节点。

(3)确定单层递归:根节点的左子树=向左递归,根节点的右子树=向右递归。

(4)最后返回根节点。

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def trimBST(self, root: Optional[TreeNode], low: int, high: int) -> Optional[TreeNode]:
        if root is None:
            return None

        if root.val < low:
            left = self.trimBST(root.right, low, high)
            return left
        if root.val > high:
            right = self.trimBST(root.left, low, high)
            return right

        root.left = self.trimBST(root.left, low, high)
        root.right = self.trimBST(root.right, low, high)
        
        return root

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

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

1. 题意

(1)为什么一定要构造成平衡二叉搜索树?

        因为任何一个有序数组,都可以将其构造为一个链式的二叉搜索树(相当于一条斜线),且满足二叉树的特性,那么本题将没有意义。

2. 整体思路

        找到数组的中间点,构造左区间和右区间,递归左区间构造为左子树,递归右区间构造为右子树。也就是数组的中点作为树的中节点,这样才能保证二叉树是平衡的。

        如果数组是偶数,那么取左侧/右侧节点作为中节点都可以;如果数组是奇数,那么直接取中间节点。

3. 代码实现

(1)定义递归函数的返回值和参数:传入数组、左区间点和右区间点(数组下标),返回值为根节点。此处需要注意区间的开闭,我们在这里定义为左闭右闭。

(2)确定终止条件:如果左区间大于右区间,就是非法区间,返回None;而左区间等于右区间不是非法区间,因为我们定义的是左闭右闭。

(3)单层递归的处理逻辑:

        首先找到中间值下标等于左区间+右区间除以2,注意要采用left+(right-left)//2的写法,否则容易超出内存。接下来构造根节点,传入中间节点的数值。

        递归左子树,传入的右区间是middle-1,因为我们定义的是闭区间。

        递归左子树,传入的左区间是middle+1,因为我们定义的是闭区间。

(4)返回根节点。

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def buildTree(self, nums, left, right)->Optional[TreeNode]:

        if left > right: 
            return None

        mid = left + (right - left) // 2
        root = TreeNode(nums[mid])

        root.left = self.buildTree(nums, left, mid-1)
        root.right = self.buildTree(nums, mid+1, right)

        return root

    def sortedArrayToBST(self, nums: List[int]) -> Optional[TreeNode]:
        root = self.buildTree(nums, 0, len(nums)-1)
        return root

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

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

1. 解题思路

理解题意:

        本题是一棵二叉搜索树,例如下图,节点顺序就是二叉搜索树的遍历顺序(左中右),题目要求每个节点值等于节点顺序大于等于它的节点值之和。比如节点5,它的值就等于节点6、7、8的值之和,也就是节点6的值加上节点5本身。

整体思路:整体的遍历顺序是右中左,采用双指针的思路,定义一个cur指针指向当前节点,pre指针指向前一个节点,然后当前节点的值就等于前一个节点的值加当前的值。

2. 代码实现

(1)定义pre表示前一个节点的值,由于从树最右边的节点开始,所以cur一开始指向的就是树最右边的叶子节点,而其前一个节点不存在,所以pre初始化为0,这样cur就等于0加上本身就等于本身的值。

(2)定义函数的参数和返回值,由于本题是遍历这棵二叉树并更新节点的值,所以本题不需要返回值,传入树的根节点。

(3)确定终止条件:遇到空节点就返回空。

(4)单层递归逻辑:遍历顺序-右中左。

        1)右:先向右子树进行递归;

        2)中:将当前节点的值加上前一个节点的值,也就是pre+cur.val,就将当前节点值更新了,然后将当前节点的新值赋给pre;

        3)左:向左子树进行递归。

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    def __init__(self):
        self.pre = 0

    def convertBST(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
        if root is None:
            return root

        # right:
        self.convertBST(root.right)

        # middle:
        root.val += self.pre
        self.pre = root.val

        # left:
        self.convertBST(root.left)
        
        return root

四、二叉树总结

好了,二叉树大家就这样刷完了,做一个总结吧
  • 涉及到二叉树的构造,无论普通二叉树还是二叉搜索树一定前序,都是先构造中节点。

  • 求普通二叉树的属性,一般是后序,一般要通过递归函数的返回值做计算。

  • 求二叉搜索树的属性,一定是中序了,要不白瞎了有序性了。

注意在普通二叉树的属性中,我用的是一般为后序,例如单纯求深度就用前序,二叉树:找所有路径 (opens new window)也用了前序,这是为了方便让父节点指向子节点。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值