Q:二叉搜索树中的两个节点被错误地交换。请在不改变其结构的情况下,恢复这棵树。
A:
1.中序遍历 根据出现降序的次数 找出两个错误的节点,交换节点值
- 只出现一次降序 则第一个为降序时较大的,第二个为降序时较小的
- 出现了两次降序 则第一个为第一次降序时较大的,第二个为第二次降序时较小的
public void inorder(TreeNode root, List<Integer> nums) {
if (root == null) return;
inorder(root.left, nums);
nums.add(root.val);
inorder(root.right, nums);
}
public int[] findTwoSwapped(List<Integer> nums) {
int n = nums.size();
int x = -1, y = -1;
for(int i = 0; i < n - 1; ++i) {
if (nums.get(i + 1) < nums.get(i)) {
y = nums.get(i + 1);
// first swap occurence
if (x == -1) x = nums.get(i);
// second swap occurence
else break;
}
}
return new int[]{x, y};
}
public void recover(TreeNode r, int count, int x, int y) {
if (r != null) {
if (r.val == x || r.val == y) {
r.val = r.val == x ? y : x;
if (--count == 0) return;
}
recover(r.left, count, x, y);
recover(r.right, count, x, y);
}
}
public void recoverTree(TreeNode root) {
List<Integer> nums = new ArrayList();
inorder(root, nums);
int[] swapped = findTwoSwapped(nums);
recover(root, 2, swapped[0], swapped[1]);
}
2.为了满足空间复杂度为O(1),使用Morris遍历算法
Morris遍历算法 具体内容查看Morris遍历算法
代码:
public void recoverTree(TreeNode root) {
//x存小索引那个结点,y存大索引那个结点,pre存前驱结点
TreeNode x = null, y = null, pred = null;
TreeNode node = root;
while (node != null) {
if (node.left == null) {
if (pred != null && node.val < pred.val) {
if (x == null)
x = pred;
y = node;
}
pred = node;
node = node.right;
} else {
TreeNode predecessor = node.left;
while (predecessor.right != null && predecessor.right != node) {
predecessor = predecessor.right;
}
if (predecessor.right == null) {
predecessor.right = node;
node = node.left;
} else {
if (pred != null && node.val < pred.val) {
if (x == null)
x = pred;
y = node;
}
pred = node;
predecessor.right = null;
node = node.right;
}
}
}
int t = x.val;
x.val = y.val;
y.val = t;
}