题目
二叉搜索树中的两个节点被错误地交换。
请在不改变其结构的情况下,恢复这棵树。
示例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) 空间复杂度的解法很容易实现。
- 你能想出一个只使用常数空间的解决方案吗?
对数组进行排序
算法实现
def recoverTree(self, root: TreeNode) -> None:
def inorder(r: TreeNode) -> List[int]:
return inorder(r.left) + [r.val] + inorder(r.right) if r else []
def find_two_swapped(nums: List[int]) -> (int, int):
n = len(nums)
x = y = -1
for i in range(n - 1):
if nums[i + 1] < nums[i]:
y = nums[i + 1]
# first swap occurence
if x == -1:
x = nums[i]
# second swap occurence
else:
break
return x, y
def recover(r: TreeNode, count: int):
if r:
if r.val == x or r.val == y:
r.val = y if r.val == x else x
count -= 1
if count == 0:
return
recover(r.left, count)
recover(r.right, count)
nums = inorder(root)
x, y = find_two_swapped(nums)
recover(root, 2)
执行结果
执行结果 : 通过
执行用时 : 116 ms, 在所有 Python3 提交中击败了13.87%的用户
内存消耗 : 13.7 MB, 在所有 Python3 提交中击败了6.12%的用户
复杂度分析
-
时间复杂度:O(n)
-
空间复杂度:O(n)
迭代中序遍历
算法实现
def recoverTree(self, root: TreeNode) -> None:
stack = []
x = y = pred = None
while stack or root:
while root:
stack.append(root)
root = root.left
root = stack.pop()
if pred and root.val < pred.val:
y = root
if not x:
x = pred
else:
break
pred = root
root = root.right
x.val, y.val = y.val, x.val
执行结果
复杂度分析
-
时间复杂度:O(n)
-
空间复杂度:O(H),其中 H 指的是树的高度。
递归中序遍历
算法实现
def recoverTree(self, root: TreeNode) -> None:
def find_two_swapped(root: TreeNode):
nonlocal x, y, pred
if root is None:
return
find_two_swapped(root.left)
if pred and root.val < pred.val:
y = root
# first swap occurence
if x is None:
x = pred
# second swap occurence
else:
return
pred = root
find_two_swapped(root.right)
x = y = pred = None
find_two_swapped(root)
x.val, y.val = y.val, x.val
执行结果
复杂度分析
-
时间复杂度:O(n)
-
空间复杂度:O(H),其中 H 指的是树的高度。
Morris 中序遍历
算法实现
def recoverTree(self, root: TreeNode) -> None:
# predecessor is a Morris predecessor.
# In the 'loop' cases it could be equal to the node itself predecessor == root.
# pred is a 'true' predecessor,
# the previous node in the inorder traversal.
x = y = predecessor = pred = None
while root:
# If there is a left child
# then compute the predecessor.
# If there is no link predecessor.right = root --> set it.
# If there is a link predecessor.right = root --> break it.
if root.left:
# Predecessor node is one step left
# and then right till you can.
predecessor = root.left
while predecessor.right and predecessor.right != root:
predecessor = predecessor.right
# set link predecessor.right = root
# and go to explore left subtree
if predecessor.right is None:
predecessor.right = root
root = root.left
# break link predecessor.right = root
# link is broken : time to change subtree and go right
else:
# check for the swapped nodes
if pred and root.val < pred.val:
y = root
if x is None:
x = pred
pred = root
predecessor.right = None
root = root.right
# If there is no left child
# then just go right.
else:
# check for the swapped nodes
if pred and root.val < pred.val:
y = root
if x is None:
x = pred
pred = root
root = root.right
x.val, y.val = y.val, x.val
执行结果
复杂度分析
-
时间复杂度:O(n)
-
空间复杂度:O(1)
小结
题解思路写的很清楚,但是刚开始自己没有写出来,完全没有想法,可能是被困难题目吓到了,看了题解将困难题目分成三个简单的子问题,先用暴力解出,再对前两步进行优化整合,得到实现中序遍历的不同方法。
主要还是对二叉树的特性不太熟悉,没想到中序遍历是一个升序序列。但首要问题是应该对题目进行分拆,把难题分成一步一步简单的问题,至少要写出暴力的解。想起之前自己总结的解题方法。。。自己却被困难吓到了,忘了步骤,反而想一步解出=。=
以后一定要注重对困难题的分拆!!!