题目:
给定二叉搜索树的根节点 root ,树中的两个节点被错误地交换。请在不改变其结构的情况下,恢复这棵树。
思路:
- 首先注意题意:两个节点错误的交换,意思不是存在两个地方乱序,而是有两个节点值互换了。
- 二叉搜索树即根节点左子树比根节点小。根节点右子树比根节点大。二叉搜索树的中序遍历是从小到大的有序集合。二叉搜索树中不包含重复元素。
- 通过这些概念,我们可以直接进行中序遍历,将元素放到List集合中,获得的就是从小到大的数据集合(不考虑节点调换)。
难点: 常规思路直接中序遍历,之后排序,再重组二叉树,但是改变了结构同时时间复杂度较高。所以我们需要直接定位哪两个元素进行了交换。这道题我也是卡在这里。通过看官方题解,看到了十分灵巧的方法。
代码:
class Solution {
public void recoverTree(TreeNode root) {
List<Integer> nums = new ArrayList<Integer>();
inorder(root, nums);
int[] swapped = findchange(nums);
recover(root, 2, swapped[0], swapped[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);
}
//找到两个交换节点的索引并获取他们的值
//十分灵巧的确定位置的方法,看不懂可以用{3,2,4,1,5}来进行模拟
public int[] findchange(List<Integer> nums) {
int n = nums.size();
int index1 = -1, index2 = -1;
for (int i = 0; i < n - 1; i++) {
if (nums.get(i + 1) < nums.get(i)) {
index2 = i + 1;
if (index1 == -1) {
index1 = i;
} else {
break;
}
}
}
int x = nums.get(index1), y = nums.get(index2);
return new int[]{
x, y};
}
public void recover(TreeNode root, int count, int x, int y) {
//使用count来作为标记,标记交换两次为界限
if (root != null) {
if (root.val == x || root.val == y) {
root.val = root.val == x ? y : x;
if (--count == 0) {
return;
}
}
recover(root.right, count, x, y);
recover(root.left, count, x, y);
}
}
}