问题描述:
给你二叉搜索树的根节点 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 使二叉搜索树有效。
提示:
树上节点的数目在范围 [2, 1000] 内
-231 <= Node.val <= 231 - 1
进阶:使用 O(n) 空间复杂度的解法很容易实现。你能想出一个只使用 O(1) 空间的解决方案吗?
解题思路
二叉搜索树的特点是: 左子树<根结点<右子树。
所以采用中序遍历时,节点的值是升序的。
我们可以找出不符合升序的两个节点,将他们的值交换。
不符合升序的两个节点分别是:
第一次不满足升序的节点的前一个
第二次不满足升序的当前节点。
代码实现
public static void recoverTree(TreeNode root) {
if (root == null) {
return;
}
Stack<TreeNode> sta = new Stack<TreeNode>();
List<TreeNode> result = new ArrayList<TreeNode>();
TreeNode p = root;
while (p != null || !sta.isEmpty()) {
if (p != null) {
sta.push(p);
p = p.left;
} else {
p = sta.pop();
result.add(p);
p = p.right;
}
}
TreeNode preBad = null, lastBad = null;
int preVal = result.get(0).val;
for (int i = 1; i < result.size(); i++) {
if (preBad == null) {
if (result.get(i).val < preVal) {
preBad = result.get(i - 1);
lastBad = result.get(i);
}
} else {
if (result.get(i).val < preVal) {
lastBad = result.get(i);
}
}
preVal = result.get(i).val;
}
swap(preBad, lastBad);
}
private static void swap(TreeNode badNodeL, TreeNode badNodeR) {
int t = badNodeL.val;
badNodeL.val = badNodeR.val;
badNodeR.val = t;
}
对于中序遍历,我们可以省去额外的list空间,遍历时进行对比
class Solution {
public void recoverTree(TreeNode root) {
if (root == null) {
return;
}
Stack<TreeNode> sta = new Stack<TreeNode>();
TreeNode preBad = null, lastBad = null, pre = null;
int preVal = Integer.MIN_VALUE;
TreeNode p = root;
while (p != null || !sta.isEmpty()) {
if (p != null) {
sta.push(p);
p = p.left;
} else {
p = sta.pop();
if (preBad == null) {
if (p.val < preVal) {
preBad = pre;
lastBad = p;
}
} else {
if (p.val < preVal) {
lastBad = p;
}
}
pre = p;
preVal = p.val;
p = p.right;
}
}
swap(preBad, lastBad);
}
private static void swap(TreeNode badNodeL, TreeNode badNodeR) {
int t = badNodeL.val;
badNodeL.val = badNodeR.val;
badNodeR.val = t;
}
}