链接:https://leetcode-cn.com/problems/recover-binary-search-tree/
二叉搜索树中的两个节点被错误地交换。
请在不改变其结构的情况下,恢复这棵树。
示例 1:
输入: [1,3,null,null,2]
1
/
3
\
2
输出: [3,1,null,null,2]
3
/
1
\
2
示例 2:
输入: [3,1,4,null,null,2]
3
/ \
1 4
/
2
输出: [2,1,4,null,null,3]
2
/ \
1 4
/
3
进阶:
使用 O(n) 空间复杂度的解法很容易实现。你能想出一个只使用常数空间的解决方案吗?
根据题意我们知道,一颗二叉搜索树的两个节点被交换了位置,导致其变成了一颗普通的二叉树。而现在,我们需要把它恢复成二叉搜索树,所以难点就在于如何找到这两个节点并对其进行交换。
想必大家都知道,二叉树搜索树中序遍历的序列是一个递增序列,我们以示列 2 中的二叉树举例,其中序遍历的序列是 [1,3,2,4],我们只要找到节点 3 和节点 2 ,然后将其交换顺序,这时就是一颗二叉搜索树了。
其实,这个序列中有个规律:
对于我们要寻找的第一个节点,此处指的是节点 3,是中序遍历时第一个 前一个节点大于后一个节点 的节点,然后我们选取前一个节点;
对于我们要寻找的第二个节点,当第一个节点找到以后, 出现 前一个节点大于后一个节点 时,我们选择后一个节点,这里是指节点 2。
时间复杂度:O(n)
空间复杂度:O(n)
Python:
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def recoverTree(self, root: TreeNode) -> None:
"""
Do not return anything, modify root in-place instead.
"""
self.firstNode=None
self.secondNode=None
self.pre=TreeNode(float('-inf'))
def helper(root):
if not root:
return
helper(root.left)
if self.firstNode is None and self.pre.val >= root.val:
self.firstNode=self.pre
if self.firstNode and self.pre.val >= root.val:
self.secondNode=root
self.pre=root
helper(root.right)
helper(root)
self.firstNode.val,self.secondNode.val=self.secondNode.val,self.firstNode.val
Go:
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func recoverTree(root *TreeNode) {
var first,second *TreeNode
pre := &TreeNode{Val:math.MinInt64}
var helper func(root *TreeNode)
helper = func(root *TreeNode){
if root == nil{
return
}
helper(root.Left)
if first == nil && pre.Val >= root.Val{
first=pre
}
if first != nil && pre.Val >= root.Val{
second=root
}
pre=root
helper(root.Right)
}
helper(root)
first.Val,second.Val=second.Val,first.Val
}
时间复杂度:O(n)
空间复杂度:O(n)
Python:
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
def recoverTree(self, root: TreeNode) -> None:
"""
Do not return anything, modify root in-place instead.
"""
firstNode=None
secondNode=None
pre=TreeNode(float('-inf'))
stack=[]
while stack or root:
while root:
stack.append(root)
root=root.left
root=stack.pop()
if not firstNode and pre.val > root.val:
firstNode=pre
if firstNode and pre.val > root.val:
secondNode=root
pre=root
root=root.right
firstNode.val,secondNode.val=secondNode.val,firstNode.val
Go:
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func recoverTree(root *TreeNode) {
stack := []*TreeNode{}
var first,second *TreeNode
pre := &TreeNode{Val:math.MinInt64}
for len(stack)>0 || root != nil{
for root != nil{
stack=append(stack,root)
root=root.Left
}
index:=len(stack)-1
root=stack[index]
stack=stack[:index]
if first == nil && pre.Val>root.Val{
first=pre
}
if first != nil && pre.Val > root.Val{
second=root
}
pre=root
root=root.Right
}
first.Val,second.Val=second.Val,first.Val
}
以上两种方法的空间复杂度都是 O(n),但是题目进阶中要求 O(1) 的时间复杂度,其实我们可以通过莫里斯遍历来实现。接下来,我们就了解下莫里斯遍历。
对于一颗拥有 n 个节点的二叉树,有 2n 个指针域,其中非空指针域有 n-1 个,空指针域有 n+1 个。通过情况下,我们只用了非空指针域,那现在如果我们把空指针域也利用起来呢?
其实我们利用空指针域的时候,就引入了一个新的概念,那就是 线索二叉搜索树。线索二叉树就是将左子节点的空指针域指向其前驱节点,右子节点的空指针域指向其后继节点,莫里斯遍历就是基于线索二叉树来实现 O(1) 的空间复杂度。
如何寻找前驱节点?
如果当前节点有左子节点,那么从左子节点一直沿着右子节点的指针,一直往右走,直到走到叶子节点为止,则这个叶子节点就是当前节点的前序节点。
让我们用上图举个例子,例如节点 11,首选移动到其左子节点 8,然后从当前节点 8 移动到其右子节点,一直移动到其叶子节点 9,所以节点 9 就是节点 11 的前序节点。
思路:
如果当前节点 root 的左子节点为空,则访问当前节点,然后以当前节点的右子节点作为当前节点,root=root.right;
如果当前节点 root 的左子节点不为空,则在当前节点 root 的左子树中去寻找当前节点 root 在中序遍历中的前驱节点;
如果前驱节点的右子节点为空,则将它的右子节点指向当前节点 root,之后访问当前节点 root 的左子节点,root=root.left;
如果前驱节点的右子节点不为空,则将其右子节点指向当前节点 root,同时表明我们已经遍历完当前节点 root 的左子树,此时将前驱节点的右子节点置为空,之后访问当前节点 root 的右子节点,root=root.right;
重复上述步骤,直到遍历完整颗树。
时间复杂度:O(n)
空间复杂度:O(1)
Python:
# 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 recoverTree(self, root: TreeNode) -> None:
"""
Do not return anything, modify root in-place instead.
"""
first,second=None,None
pre=None
predecessor=None
while root:
if root.left:
predecessor=self.getPredecessor(root)
if not predecessor.right:
predecessor.right=root
root=root.left
else:
predecessor.right=None
if pre and pre.val > root.val:
if not first:
first=pre
second=root
else:
second=root
pre=root
root=root.right
else:
if pre and pre.val > root.val:
if not first:
first=pre
second=root
else:
second=root
pre=root
root=root.right
first.val,second.val=second.val,first.val
def getPredecessor(self, root: TreeNode) -> None:
pre=None
if root.left:
pre=root.left
while pre.right and pre.right != root:
pre=pre.right
return pre
Go:
/**
* Definition for a binary tree node.
* type TreeNode struct {
* Val int
* Left *TreeNode
* Right *TreeNode
* }
*/
func recoverTree(root *TreeNode) {
var first,second *TreeNode
var predecessor *TreeNode
pre := &TreeNode{Val:math.MinInt64}
for root != nil{
if root.Left != nil{
predecessor=getPredecessor(root)
if predecessor.Right == nil{
predecessor.Right=root
root=root.Left
}else{
predecessor.Right=nil
if pre != nil && pre.Val > root.Val{
if first == nil{
first=pre
second=root
}else{
second=root
}
}
pre=root
root=root.Right
}
}else{
if pre != nil && pre.Val > root.Val{
if first == nil{
first=pre
second=root
}else{
second=root
}
}
pre=root
root=root.Right
}
}
first.Val,second.Val=second.Val,first.Val
}
func getPredecessor(root *TreeNode) *TreeNode {
var pre *TreeNode
if root.Left != nil{
pre=root.Left
for pre.Right != nil && pre.Right != root{
pre=pre.Right
}
}
return pre
}
如果觉得文章不错,希望大家可以扫描上方名片关注我的微信公众号噢,点赞、收藏、在看、分享就再好不过了。如果有任何建议和问题,可以在下方给我留言,我会及时回复的,同时会不定期更新更多的文章,祝我们终将自由。