这是树的第二篇算法,力扣链接。
给你二叉搜索树的根节点
root
,该树中的 恰好 两个节点的值被错误地交换。请在不改变其结构的情况下,恢复这棵树 。示例 1:
输入:root = [1,3,null,null,2] 输出:[3,1,null,null,2] 解释:3 不能是 1 的左孩子,因为 3 > 1 。交换 1 和 3 使二叉搜索树有效。示例 2:
输入:root = [3,1,4,null,null,2] 输出:[2,1,4,null,null,3] 解释:2 不能在 3 的右子树中,因为 2 < 3 。交换 2 和 3 使二叉搜索树有效。
上次算法提到了中序遍历:
中序遍历 (Inorder Traversal)
在中序遍历中,我们按照以下顺序遍历树中的节点:
- 遍历左子树
- 访问根节点
- 遍历右子树
对于二叉搜索树(BST),中序遍历会按照升序访问所有节点,因为二叉搜索树的特点是左子节点的值小于根节点的值,根节点的值小于右子节点的值。
中序遍历会将树一维化并且是有序的:
假设有一棵二叉树如下:
A / \ B C / \ \ D E F
对这棵树进行不同的遍历会得到以下结果:
- 中序遍历:
D, B, E, A, C, F
。首先遍历左子树(D, B, E),然后是根节点(A),最后是右子树(C, F)。
因此这里的第一个解法是将树一维化,然后找到非有序的节点,交换两个节点位置即可。
func recoverTree(root *TreeNode) {
nodeList := make([]*TreeNode, 0)
stack := make([]*TreeNode, 0)
cur := root
for len(stack) > 0 || cur != nil {
for cur != nil {
stack = append(stack, cur)
cur = cur.Left
}
cur = stack[len(stack)-1]
stack = stack[:len(stack)-1]
nodeList = append(nodeList, cur)
cur = cur.Right
}
index1, index2 := -1, -1
for i := 0; i < len(nodeList)-1; i++ {
if nodeList[i+1].Val < nodeList[i].Val {
index2 = i + 1
if index1 == -1 {
index1 = i
} else {
break
}
}
}
nodeList[index1].Val, nodeList[index2].Val = nodeList[index2].Val, nodeList[index1].Val
}
这道题的进阶方法是不用额外的空间复杂度。
先不讲这道题怎么做,先试一下不用额外空间复杂度的中序遍历。这个做法叫做Morris 中序遍历,其核心思路是利用叶子节点右侧的空指针指向中序遍历的后继节点,从而避免了栈的使用。
操作思路是首先检查当前节点的左子节点。如果左子节点不存在,我们输出当前节点并将其右子节点作为下一个当前节点。如果左子节点存在,我们找到当前节点在其左子树上的前驱节点,这是其左子树中最右侧的节点。如果前驱节点的右子节点为空,我们将其设置为当前节点,并将当前节点更新为其左子节点。如果前驱节点的右子节点是当前节点,我们将其重新设置为空,输出当前节点,并将当前节点更新为其右子节点。
这种方式的遍历确保了每个节点都被访问两次,但只有在第二次访问时才会输出。在整个过程中,我们没有使用栈或递归,从而实现了常数空间复杂度。
这个做法很神奇,不求一次性就能记住,多少留个印皙那个每次都可以回看:
func morrisTraversal(root *TreeNode) {
nodeList := make([]*TreeNode, 0)
cur := root
for cur != nil {
if cur.Left == nil {
nodeList = append(nodeList, cur)
cur = cur.Right
} else {
pre := cur.Left
for pre.Right != nil && pre.Right != cur {
pre = pre.Right
}
if pre.Right == nil {
pre.Right = cur
cur = cur.Left
} else {
pre.Right = nil
nodeList = append(nodeList, cur)
cur = cur.Right
}
}
}
}
至于这道题怎么利用morris解,最简单的当然还是用数组找索引,但是为了不用额外空间,我们可以考虑原地更改,不过需要记录索引。
func recoverTree(root *TreeNode) {
var first, second, prev *TreeNode
cur := root
for cur != nil {
if cur.Left == nil {
if prev != nil && cur.Val < prev.Val {
if first == nil {
first = prev
}
second = cur
}
prev = cur
cur = cur.Right
} else {
pre := cur.Left
for pre.Right != nil && pre.Right != cur {
pre = pre.Right
}
if pre.Right == nil {
pre.Right = cur
cur = cur.Left
} else {
pre.Right = nil
if prev != nil && cur.Val < prev.Val {
if first == nil {
first = prev
}
second = cur
}
prev = cur
cur = cur.Right
}
}
}
first.Val, second.Val = second.Val, first.Val
}