leetcode树之二叉搜索树的操作

1008、前序遍历构造二叉搜索树

给定一个整数数组,它表示BST(即 二叉搜索树 )的 先序遍历 ,构造树并返回其根。

保证 对于给定的测试用例,总是有可能找到具有给定需求的二叉搜索树。

二叉搜索树 是一棵二叉树,其中每个节点, Node.left 的任何后代的值 严格小于 Node.val , Node.right 的任何后代的值 严格大于 Node.val。

二叉树的 前序遍历 首先显示节点的值,然后遍历Node.left,最后遍历Node.right。

示例1:

输入:preorder = [8,5,1,7,10,12]
输出:[8,5,10,1,7,null,12]

示例2:

输入: preorder = [1,3]
输出: [1,null,3]

思路1

给到的是前序遍历序列,而二叉搜索树中序遍历是有序的,这个时候我们可以给数组排序,然后该问题就转化为通过前序和中序遍历创造二叉树的问题了,不记得的话可以看之前写过的二叉树的创建

class Solution:
    def bstFromPreorder(self, preorder: List[int]) -> Optional[TreeNode]:
        if not preorder: return None
        inorder = sorted(preorder)
        index = {e: i for i, e in enumerate(inorder)}
        def buildTree(pre_left, pre_right, in_left, in_right):
            root = TreeNode(preorder[pre_left])
            if pre_left >= pre_right: return root
            root_index = index[root.val]
            if root_index > in_left:
                new_pre_left = pre_left + 1
                new_pre_right = new_pre_left + root_index - in_left -1
                root.left = buildTree(new_pre_left, new_pre_right, in_left, root_index - 1)
            if in_right > root_index:
                new_pre_left = pre_left + 1+ root_index - in_left
                root.right = buildTree(new_pre_left, pre_right, root_index + 1, in_right)
            return root
        n = len(preorder)
        return buildTree(0, n-1, 0, n-1)

思路2:

由于前序序列的特点,[root, [左子树序列], [右子树序列]],并且结合二叉搜索树的特点,左子树序列的值都小于root的值,右子树序列的值都大于root的值,这样,就可以区分左子树和右子树,通过递归构造出二叉搜索树。

class Solution:
    def bstFromPreorder(self, preorder: List[int]) -> Optional[TreeNode]:
        if not preorder: return None
				# 需要递归构造树的起始坐标和终点坐标
        def dfs(start, end):
            root_val = preorder[start]
            root = TreeNode(root_val)
            if start == end: return root
            # 这里遍历找到第一个比根节点大的值,有可能找不到,那么就让mid = end + 1
            mid = None
            for i in range(start + 1, end + 1):
                if preorder[i] > root_val:
                    mid = i
                    break
            if not mid:
                mid = end + 1
            # 由于这里的mid是第一个比root节点大的值,所以是右子序列的第一个值
            # start + 1代表的是左子树的起始值
            # 如果mid > start + 1说明存在左子树,那么进行构建
            if mid > start + 1:
                root.left = dfs(start + 1, mid - 1)
            # 如果end大于等于mid说明存在右子树,进行构建
            if end >= mid:
                root.right = dfs(mid, end)
            return root
        n = len(preorder)
        return dfs(0, n-1)

思路3:

维护一个栈,如果遍历到的数比栈顶的数小,那么这个值一定是栈顶节点的左子树,如果比栈顶大,那么将小于该数的节点出栈,那么栈顶一定是比这个值要大的,或者栈为空。

如果栈顶比这个值大,那么这个值一定在最后一次弹出的节点值和栈顶值之间,也就是说这个值其实是最后一次出栈节点的右子节点值

如果栈空了,那说明这个值最大,那么这个值是最后一次出栈节点的右子节点值

class Solution:
    def bstFromPreorder(self, preorder: List[int]) -> Optional[TreeNode]:
        if not preorder: return None
        n = len(preorder)
        root = TreeNode(preorder[0])
        stack = [root]
        for i in range(1, n):
            node = stack[-1]
            cur_val = preorder[i]
            # 将所有比当前值小的节点出栈,并记录最后一次出栈的节点
            while stack and stack[-1].val < cur_val:
                node = stack.pop()
            cur = TreeNode(cur_val)
            # 根据节点值大小放到左子节点或右子节点
            if node.val < cur_val:
                node.right = cur
            else:
                node.left = cur
            stack.append(cur)
        return root
700、二叉搜索树的搜索

给定二叉搜索树(BST)的根节点 root 和一个整数值 val。

你需要在 BST 中找到节点值等于 val 的节点。 返回以该节点为根的子树。 如果节点不存在,则返回 null 。

示例1:

输入:root = [4,2,7,1,3], val = 2
输出:[2,1,3]

示例2:

输入:root = [4,2,7,1,3], val = 5
输出:[]

思路

由于二叉搜索树的特点,左子树节点 < 根节点值 < 右子树节点值,所以可以用二分的方法解决。

递归

class Solution:
    def searchBST(self, root: TreeNode, val: int) -> TreeNode:
        if not root: return root
        if root.val == val: return root
        return self.searchBST(root.left, val) if root.val > val else self.searchBST(root.right, val)

迭代

class Solution:
    def searchBST(self, root: TreeNode, val: int) -> TreeNode:
        if not root: return root
        cur = root
        while cur:
            if cur.val == val: return cur
            cur = cur.left if cur.val > val else cur.right
        return None
701、二叉搜索树种的插入

给定二叉搜索树(BST)的根节点 root 和要插入树中的值 value ,将值插入二叉搜索树。 返回插入后二叉搜索树的根节点。 输入数据 保证 ,新值和原始二叉搜索树中的任意节点值都不同。

注意,可能存在多种有效的插入方式,只要树在插入后仍保持为二叉搜索树即可。 你可以返回 任意有效的结果 。

示例1:

输入:root = [4,2,7,1,3], val = 5
输出:[4,2,7,1,3,5]

示例2:

输入:root = [40,20,60,10,30,50,70], val = 25
输出:[40,20,60,10,30,50,70,null,null,25]

示例3:

输入:root = [4,2,7,1,3,null,null,null,null,null,null], val = 5
输出:[4,2,7,1,3,5]

思路:

根据二叉搜索树的特性,小的值当做左子节点,大的值当做右子节点

递归

class Solution:
    def insertIntoBST(self, root: TreeNode, val: int) -> TreeNode:
        if not root: return TreeNode(val)
        if root.val > val:
            root.left = self.insertIntoBST(root.left, val)
        else:
            root.right = self.insertIntoBST(root.right, val)
        return root

迭代

class Solution:
    def insertIntoBST(self, root: TreeNode, val: int) -> TreeNode:
        if not root: return TreeNode(val)
        cur = root
        while cur:
            if cur.val > val:
              	# 如果当前有左子节点,那么继续遍历,没有就将值插入到左节点上
                if cur.left:
                    cur = cur.left
                else:
                    cur.left = TreeNode(val)
                    break
            else:
              	# 如果当前有右子节点,那么继续遍历,没有就将值插入到右节点上
                if cur.right:
                    cur = cur.right
                else:
                    cur.right = TreeNode(val)
                    break
        return root
450、删除二叉搜索树中的节点

给定一个二叉搜索树的根节点 root 和一个值 key,删除二叉搜索树中的 key 对应的节点,并保证二叉搜索树的性质不变。返回二叉搜索树(有可能被更新)的根节点的引用。

一般来说,删除节点可分为两个步骤:

首先找到需要删除的节点;
如果找到了,删除它。

示例1:

输入:root = [5,3,6,2,4,null,7], key = 3
输出:[5,4,6,2,null,null,7]

示例2:

输入: root = [5,3,6,2,4,null,7], key = 0
输出: [5,3,6,2,4,null,7]

示例3:

输入: root = [], key = 0
输出: []

思路:

首先可以用递归方式找到要删除的节点,这个时候就会出现三种情况,

  • 该节点没有子节点,那么直接删除即可
  • 如果该节点有左子节点,那么需要找到左子树中的最大值, 这个值是仅次于当前值,第二小的值,称为前驱节点吧,将该节点的值设置为前驱节点的值,并且删掉前驱节点。
  • 如果该节点没有左子节点但是有右子节点,那么需要在右子树中找到最小的值,称为后继节点吧,也是赋值,删除节点的操作即可。
class Solution:
    # 找到前驱节点
    def precursor(self, root):
        cur = root.left
        while cur.right:
            cur = cur.right
        return cur.val
    # 后继节点
    def successor(self, root):
        cur = root.right
        while cur.left:
            cur = cur.left
        return cur.val

    def deleteNode(self, root: Optional[TreeNode], key: int) -> Optional[TreeNode]:
        if not root: return None
        # 如果节点的值和key不等,继续遍历找到要删除的节点
        if root.val > key:
            root.left = self.deleteNode(root.left, key)
        elif root.val < key:
            root.right = self.deleteNode(root.right, key)
        # 如果相等,该节点应该是要被删除的节点
        else:
          	# 如果没有子节点,直接删除该节点,将其设置为None即可
            # 由于deleteNode返回的是节点值,那么设置为None,这个值会被删掉
            if not root.left and not root.right:
                root = None
            # 如果有左子节点,找到前驱节点,删除前驱节点
            elif root.left:
                root.val = self.precursor(root)
                root.left = self.deleteNode(root.left, root.val)
            # 如果有右子节点,找到后继节点,删除后继节点
            elif root.right:
                root.val = self.successor(root)
                root.right = self.deleteNode(root.right, root.val)
        return root
1382、将二叉搜索树变平衡

给你一棵二叉搜索树,请你返回一棵 平衡后 的二叉搜索树,新生成的树应该与原来的树有着相同的节点值。

如果一棵二叉搜索树中,每个节点的两棵子树高度差不超过 1 ,我们就称这棵二叉搜索树是 平衡的 。

如果有多种构造方法,请你返回任意一种。

示例1:

输入:root = [1,null,2,null,3,null,4,null,null]
输出:[2,1,3,null,null,null,4]

思路:

如果直接在原有二叉树的基础上进行平衡,比较困难,可以使用中序遍历之后,通过二分法进行二叉树的创建即可。可以参考二叉树的遍历

class Solution:
  	# 中序遍历
    def inorder(self, root):
        stack = []
        cur = root
        ans = []
        while cur or stack:
            while cur:
                stack.append(cur)
                cur = cur.left
            cur = stack.pop()
            ans.append(cur.val)
            cur = cur.right
        return ans

    def balanceBST(self, root: TreeNode) -> TreeNode:
        if not root: return root
        # 中序遍历数组
        in_arr = self.inorder(root)
        def dfs(start, end):
            mid = (start + end) >> 1
            # 取中间值作为根节点,如果start和end相等就返回
            node = TreeNode(in_arr[mid])
            if start == end: return node
            # 如果mid > start就说明有左子节点,进行迭代
            if mid > start:
                node.left = dfs(start, mid - 1)
            # 如果end > mid就说明有右子节点,进行迭代
            if end > mid:
                node.right = dfs(mid + 1, end)
            return node
        return dfs(0, len(in_arr) -1)
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

溪语流沙

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值