二叉搜索树是一种所有左子树小于其父节点,右子树大于其父节点的特殊二叉树。
二叉搜索树主要用来出检索某个节点。【用于搜索】
二叉搜索树按照中序遍历,就能够得出正确从小到大的序列排序。
考点:二叉搜索树的性质。首先要知道的就是二叉搜索树中序遍历,就是从小到大的序列排序。那么就可以遍历一遍,然后输出k大就行了。BFS+list.sort() 是解法1,很慢。
解法2:递归和非递归的中序遍历
class Solution:
def kthLargest(self, root: TreeNode, k: int) -> int:
#########非递归##########
if not root: return 0
L = []
q = []
node = root
while(node or q):
if node: ## while(node):q.append(node) node.left
q.append(node)
node= node.left
else:
node = q.pop() #最深的那个
L.append(node.val) #最左
node = node.right #转到右子树
if len(L)
return L[-k] ##从小到大的
############递归####################
if not root :return 0 #
L = []
def t(node):
if node.left:
t(node.left)
L.append(node.val)
if node.right:
t(node.right)
t(root)
return L[-k]
解法3(解法2的优化)
解法2 O(N) O(N) 因为求解的是K大,右子树的值肯定要比左子树大,所以可以按照右-中-左的顺序遍历,当list长度等于K时,立即停止,以节省时间。
class Solution:
def kthLargest(self, root: TreeNode, k: int) -> int:
##非递归版本 @JustYou
if not root :return 0
q = []
count = 0
node = root ##不改变原结构
while(node or q):
while(node): #直接找到最右的
q.append(node)
node = node.right #从右开始
if q: #添加最右的信息
node = q.pop() #最深的那个
count +=1
if count == k : return node.val ##不用保存前k-1个节点,以节省空间
node = node.left #转到左子树
####递归版本##########
#递归版本
if not root:return 0 #没有结果返回
self.count = 0 ##全局
self.ans = 0
def t(node,k):
if node:
t(node.right,k)
self.count += 1 ## 从上到下
if self.count == k:
self.ans = node.val
return
t(node.left,k)
t(root,k)
return self.ans
二叉搜索树的正确后序遍历的倒序 是 根节点,右子树,左子树。那么按照搜索树的关系,那么右子树中的每个节点应该大于根节点。左子树中的每个节点都应该小于根节点。那么根据根节点可以先将左右子树分开,然后再继续在每颗子树上面,继续做递归操作。
自己写的:time 80%。
class Solution:
def verifyPostorder(self, postorder: List[int]) -> bool:
# 二叉树的后序遍历
order = postorder[::-1] # 倒数排序
# 根 右 左
def judge(order):
if len(order) == 0:return True
index = order[0]
i = 1
while(i<len(order)):
if order[i] > index:
i+=1
else:
break
left = order[i:] ## 划分左右子树
right = order[1:i]
for i in range(0,len(left)):
if left[i] > index:return False ## 判断此轮是否满足条件
for j in range(0,len(right)):
if right[j] < index:return False
return judge(left) and judge(right) ## 此轮满足前提下,再继续深入判断,左右都满足才可以
return judge(order)
看了评论和题解。总结一下不足:
(1)没必要占用left 和 right 来实现左右划分,直接利用i的值就可以进行划分,传入下一轮order[i:] order[1:i]
(2) 没必要在判断右子树是否满足条件,因为它肯定是满足的,就是按照i划分的。只需要判断左子树是否满足条件就行。
改后:
class Solution:
def verifyPostorder(self, postorder: List[int]) -> bool:
# 二叉树的后序遍历
order = postorder[::-1] # 倒数排序
# 根 右 左
def judge(order):
if len(order) == 0:return True
index = order[0]
i = 1
while(i<len(order)):
if order[i] < index:break
i+=1
#left = order[i:] ## 划分左右子树
#right = order[1:i]
#for i in range(0,len(left)):
#if left[i] > index:return False ## 判断此轮是否满足条件
for j in range(i,len(order)):
if order[j] > index:return False
return judge(order[i:]) and judge(order[1:i])
## 此轮满足前提下,再继续深入判断,左右都满足才可以
return judge(order)
(3)后序遍历的顺序是 左 右 根 那么不用倒序,直接利用最后一个节点来区分左右子树判断就行。
下面代码参考了此题评论中的@Mars的思路。 time:98%。
def judge(nums):
if len(nums) == 0:return True
index = nums[-1]
for i in range(0,len(nums)):
if nums[i] > index:break # 分隔出了左子树
for j in range(i,len(nums)):
if nums[j] < index:return False # 继续分隔右子树,如果不满足条件直接跳出,满足完成此次划分
return judge(nums[:i]) and judge(nums[i:-1]) # 左子树,右子树 必须全部满足
return judge(postorder)
(4)如果按照后序遍历的逆序是 root->right>left。那么可以构造一个单调栈来维护。思想来自评论中的此题解@失火的夏天。代码来自:此链接。
主要思想就是:后序遍历的倒序 ->根 右 左。那么刚开始是一个单调增的单调栈,后面是一个单调减的单调栈。在正确的情况下,入栈的元素一定小于根节点。(第一层的根节点设置为 正无穷)。每一次转换单调增/减时,重新寻找root。如果完成了,就是正确的,否则错误。
class Solution:
def verifyPostorder(self, postorder: [int]) -> bool:
stack, root = [], float("+inf")
for i in range(len(postorder) - 1, -1, -1):
if postorder[i] > root: return False
while(stack and postorder[i] < stack[-1]):
root = stack.pop()
stack.append(postorder[i])
return True