我的LeetCode代码仓:https://github.com/617076674/LeetCode
原题链接:https://leetcode-cn.com/problems/recover-binary-search-tree/description/
题目描述:
知识点:二叉搜索树、中序遍历
思路一:中序遍历
这个思路和LeetCode098——验证二叉搜索树中的思路二是一致的。
对于一棵二叉搜索树而言,其中序遍历的结果是一个递增序列。我们保存原二叉搜索树中序遍历的结果。再对该结果进行排序后得到另一个序列,比较两个序列中的不同的两个值,即为需要交换的两个错误节点。
这里我采用的是递归的形式实现中序遍历,更多中序遍历算法请见LeetCode094——二叉树的中序遍历。
时间复杂度是O(nlogn),其中n为树中的节点个数。空间复杂度也是O(n)。
JAVA代码:
public class Solution {
List<TreeNode> list;
public void recoverTree(TreeNode root) {
list = new ArrayList<>();
inorderTraversal(root);
List<TreeNode> tempList = new ArrayList<>(list);
Collections.sort(tempList, new Comparator<TreeNode>() {
@Override
public int compare(TreeNode treeNode1, TreeNode treeNode2) {
return treeNode1.val - treeNode2.val;
}
});
List<Integer> wrongList = new ArrayList<>();
for(int i = 0; i < list.size(); i++){
if(list.get(i).val != tempList.get(i).val){
wrongList.add(i);
}
}
change(list, wrongList.get(0), wrongList.get(1));
}
private void inorderTraversal(TreeNode root){
if(root == null){
return;
}
inorderTraversal(root.left);
list.add(root);
inorderTraversal(root.right);
}
private void change(List<TreeNode> list, int i, int j){
Integer temp = list.get(i).val;
list.get(i).val = list.get(j).val;
list.get(j).val = temp;
}
}
LeetCode解题报告:
思路二:思路一的改进
在思路一中,我们是通过对中序遍历所得结果进行排序,来比较两个序列的不同值来找出需要交换的两个错误节点。众所周知,排序的时间复杂度是O(nlogn),其中n为二叉搜索树中的节点个数。
我们能否省去这个排序过程,直接在原中序遍历所得序列上确定需要交换的两个错误节点呢?
对于一课正常二叉搜索树的中序遍历结果,一定是一个递增序列,比如[1, 2, 3, 4, 5]。如果我交换两个节点的位置,有两种情况,这两个节点相邻或者不相邻。
(1)如果被交换的两个节点是相邻的,假设我们交换2和3,那么新序列就是[1, 3, 2, 4, 5]。这时我们发现出现了一个降序[3, 2],这个降序中的两个节点就是错误节点。
(2)如果被交换的两个节点是不相邻,假设我们交换2和4,那么新序列就是[1, 4, 3, 2, 5]。这时我们发现出现了两个降序[4, 3]和[3, 2]。第一个降序中的前一个节点和第二个降序中的后一个节点就是错误节点。
省去了排序,因此时间复杂度和空间复杂度均是O(n)。
JAVA代码:
public class Solution {
List<TreeNode> list;
public void recoverTree(TreeNode root) {
list = new ArrayList<>();
inorderTraversal(root);
List<Integer> wrongList = new ArrayList<>();
int countDown = 0;
for(int i = 1; i < list.size(); i++){
if(list.get(i).val < list.get(i - 1).val){
if(countDown == 0){
wrongList.add(i - 1);
wrongList.add(i);
}
wrongList.set(1, i);
countDown++;
}
}
change(list, wrongList.get(0), wrongList.get(1));
}
private void inorderTraversal(TreeNode root){
if(root == null){
return;
}
inorderTraversal(root.left);
list.add(root);
inorderTraversal(root.right);
}
private void change(List<TreeNode> list, int i, int j){
Integer temp = list.get(i).val;
list.get(i).val = list.get(j).val;
list.get(j).val = temp;
}
}
LeetCode解题报告:
思路三:思路二的改进
这个思路和LeetCode098——验证二叉搜索树中的思路三是一致的。
思路二把中序遍历的结果都保存在了一个List集合里,在遍历完成之后寻找降序序列。
事实上,我们在思路二中寻找降序序列的过程,其实只和中序遍历的当前节点和当前节点的前一个节点有关系,因此,我们完全可以在中序遍历的过程中就对结果进行验证。
这里我采用的是递归的形式实现中序遍历,更多中序遍历算法请见LeetCode094——二叉树的中序遍历。
时间复杂度是O(n),其中n为树中的节点个数。空间复杂度即递归深度,是O(h),其中h为树的高度。
JAVA代码:
public class Solution {
TreeNode wrongTreeNode1 = null;
TreeNode wrongTreeNode2 = null;
TreeNode pre = null;
int countDown = 0;
public void recoverTree(TreeNode root) {
inorderTraversal(root);
int temp = wrongTreeNode1.val;
wrongTreeNode1.val = wrongTreeNode2.val;
wrongTreeNode2.val = temp;
}
private void inorderTraversal(TreeNode root){
if(root == null){
return;
}
inorderTraversal(root.left);
if(pre != null){
if(root.val < pre.val){
if(countDown == 0) {
wrongTreeNode1 = pre;
}
wrongTreeNode2 = root;
countDown++;
}
}
pre = root;
inorderTraversal(root.right);
}
}
LeetCode解题报告: