首先我觉得我们有必要了解一些二叉搜索树的基本特征,可以参考
数据结构(二):二叉搜索树(Binary Search Tree)
其中还有一个很重要的特征我们需要单独放在这提醒自己:BST 的中序遍历结果是有序的(升序)。
我们先来看一道常规题目:
230. 二叉搜索树中第K小的元素
利用上面的性质,我们很容易就想到直接构造中序遍历结果如何返回第K大的元素:
class Solution:
def kthSmallest(self, root: Optional[TreeNode], k: int) -> int:
res = []
def inorder(root):
if not root:return
inorder(root.left)
res.append(root.val)
inorder(root.right)
inorder(root)
return res[k-1]
这样的话每一个元素都需要被遍历一遍,时间和空间复杂度都是O(N),虽然解决了问题,但终归不是一个最优解。
我们用迭代的方法尝试一遍呢?
class Solution:
def kthSmallest(self, root: Optional[TreeNode], k: int) -> int:
# 如果用迭代来做呢,比较难理解
stack = []
print(root)
while True:
# 找到左下侧那个没有左子树的节点
while root:
stack.append(root)
root = root.left
root = stack.pop()
# root看当前节点是不是要找的节点,如果是的话,返回根节点,如果不是,再看一下右子树
k -= 1
if not k:
return root.val
root = root.right
这样的方法不太好理解,而且复杂度是O(H+k),所以还是不够好。有一种比较好的解决方案是,记录每个节点元素是第m大,然后比较k和m的大小,利用左子树小于根节点小于右子树的特性。可以将复杂度降到log(N),但是这样需要维护【每个节点以自己为根的这棵二叉树有多少个节点】这个值。那么针对递归的方法,我们能不能让它不要遍历所有节点就找到我们要的值从而适当减少复杂度呢。答案是可以的。
class Solution:
def kthSmallest(self, root: Optional[TreeNode], k: int) -> int:
res = []
def inorder(root):
if not root:return
inorder(root.left)
if len(res)==k:return
res.append(root.val)
inorder(root.right)
inorder(root)
return res[-1]
当然也可以不用额外数组,仅用变量来保存最后的结果
class Solution:
def kthSmallest(self, root: Optional[TreeNode], k: int) -> int:
self.cnt = 0
self.res = None
def inorder(root):
if not root:return
inorder(root.left)
if self.cnt==k:return
self.res = root.val
self.cnt+=1
inorder(root.right)
inorder(root)
return self.res
咱来继续:
538. 把二叉搜索树转换为累加树
这一题的思路主要是这样的:对于二叉树的题而言,我们经常考虑的是对某一个节点要做什么操作,而在本题中我们很难从这方面下手,但是利用BST的特性,我们就能很好的知道:可以从大到小降序打印 BST 节点的值,如果维护一个外部累加变量sum
,然后把sum
赋值给 BST 中的每一个节点,不就将 BST 转化成累加树了吗?
于是我们尝试写一下代码,注意我们需要的是降序的结果,所以需要改变递归顺序。
class Solution:
def convertBST(self, root: Optional[TreeNode]) -> Optional[TreeNode]:
self.sum = 0
def traverse(root):
if not root:return
traverse(root.right)
self.sum += root.val
root.val = self.sum
traverse(root.left)
traverse(root)
return root