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)