lc_rt_99_recoverTree

题目:恢复二叉搜索树 hard

二叉搜索树中的两个节点被错误地交换。
请在不改变其结构的情况下,恢复这棵树。

示例 1:
输入: [1,3,null,null,2]

   1
  /
 3
  \
   2

输出: [3,1,null,null,2]

   3
  /
 1
  \
   2

示例 2:
输入: [3,1,4,null,null,2]

  3
 / \
1   4
   /
  2

输出: [2,1,4,null,null,3]

  2
 / \
1   4
   /
  3

进阶:

    使用 O(n) 空间复杂度的解法很容易实现。
    你能想出一个只使用常数空间的解决方案吗?

package leetCode.DFS;

import java.util.ArrayDeque;
import java.util.Stack;

public class lc_rt_99_recoverTree{

/*
思路:

步骤:
1)先找到两个交换的节点-逆序对
    二叉搜索树的中序遍历结果可以等效于一个升序数组
    因此先用数组举例,如果原始的二叉搜索树为
    1, 2, 3, 4, 5
    如果将其中2,4两个元素进行交换,变成
    1, 4, 3, 2, 5
    那么我们可以使用双指针的方法,检查这个数组里的逆序对,将逆序对找出来就可以解决问题
    观察数组
    第一对逆序对4, 3,是索引小的那个是被交换元素
    第二对逆序对3, 2,是索引大的那个是被交换元素
    所以我们在遇到逆序对的时候,如果是第一次遇见,则存储索引小的那个,如果不是,则存储索引大的那个

    if(pre != NULL && root.val < pre.val){
        if(s1 == NULL) s1 = pre;  // 索引小的已经找到,就不再改变
        s2 = cur;
    }

    为什么这里s2用的不是else?
        因为存在两个相邻元素交换的情况,所以我们在第一次遇见的把索引大的那个也存下来
        如果后面没有逆序对了,那就是这一对
        如果后面还有逆序对,会覆盖s2(因为有if判断,所以s1不会被覆盖)

2)找到交换的两个节点后就直接交换结点的值

可以采用:
1.递归中序遍历
2.迭代(非递归)中序遍历
3.mirror中序遍历(更好的空间复杂度,但暂时没去研究):见以下链接
https://leetcode-cn.com/problems/recover-binary-search-tree/solution/zhong-xu-bian-li-shuang-zhi-zhen-zhao-ni-xu-dui-mo/

*/


//方法1:递归中序遍历

    TreeNode pre = null;//存储前一个结点
    TreeNode x = null;//存储需要交换的一个节点
    TreeNode y = null;//存储另一个节点

    public void recoverTree(TreeNode root) {
        midOrder(root);
        swap(x, y);
    }

    //    方法2:迭代中序遍历,即非递归方式的中序遍历
    public void recoverTree2(TreeNode root) {
        ArrayDeque<TreeNode> stack = new ArrayDeque<>();
        while (root != null || !stack.isEmpty()) {
            if (root != null) {
                //注意这里使用的是add,不是push,ArrayDeque实际是个队列不是stack
                stack.add(root);
                root = root.left;
            } else {
                TreeNode node = stack.removeLast();
                if (pre != null && node.val < pre.val) {
                    y = node;
                    if (x == null)
                        x = pre;
                    else
                        break;
                }
                pre = node;
                root = node.right;
            }
        }
        swap(x, y);
    }

    /*
    中序遍历,找出需要交换的x和y节点
     */
    public void midOrder(TreeNode root) {
        if (root == null)
            return;
        midOrder(root.left);
        if (pre != null && root.val < pre.val) {
            y = root;
            if (x == null)//第一次如果已经找到x,第二次判断时x非空,不会被重置
                x = pre;
            else
                return;//第二次x!=null,y重新赋值后,两个结点都已经找到,不用继续遍历,结束
        }
        pre = root;
        midOrder(root.right);

    }

    /*
   交换两节点的值
    */
    public void swap(TreeNode x, TreeNode y) {
        int t = x.val;
        x.val = y.val;
        y.val = t;
    }

    //内部类
    class TreeNode {
        int val;
        TreeNode left;
        TreeNode right;

        TreeNode() {
        }

        TreeNode(int val) {
            this.val = val;
        }

        TreeNode(int val, TreeNode left, TreeNode right) {
            this.val = val;
            this.left = left;
            this.right = right;
        }
    }

}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值