二叉树进阶之搜索二叉树的判断与找出搜索二叉树中出错的结点

转载请注明原文地址:http://www.cnblogs.com/ygj0930/p/6611552.html 

    搜索二叉树

   树的每个结点:左儿子比当前结点小,右结点比当前结点大,则该树为搜索二叉树。

   搜索二叉树有很多种实现:常见的有BST二叉查找树、平衡搜索二叉树(AVL树)、红黑树,这些数据结构通过不同的特性约束使得数据的存储和查抄更高效。这里不再展开。

   观察发现,搜索二叉树的中序遍历结果,是一个 递增的序列。

   由此,判断一棵树是否为搜索二叉树的方法就简单了:中序遍历这个树,每次遍历一个结点时与上一个遍历结点值比较,如果小于上一个遍历的结点则说明树不是二叉搜索树,返回false,终止遍历。

    public boolean chk(TreeNode root) {
        //用一个map来保存上一次遍历时的结点值,遍历过程中通过同一个key不断修改最新的pre值
        Map<String,Integer> pre_map=new HashMap<String,Integer>();
        Stack<TreeNode> nodes=new Stack<TreeNode>();
        nodes.push(root);
        //中序遍历二叉树
        while(!nodes.isEmpty()){
            //循环处理左子树
            while(root!=null){
                root=root.left;
                if(root!=null)
                nodes.push(root);
            }
            //左子树入栈完毕,开始处理栈顶
            root=nodes.pop();
            //第一个弹出的结点,存入map
            if(pre_map.size()<1){
                pre_map.put("pre",root.val);
            }else{
                //后面弹出的结点与map保存的前一遍历结点值比较             
                int pre_val=pre_map.get("pre");
                if(root.val<pre){
                    return false;
                }else{
                    //如果大于,则更新pre值,作为下一个弹出结点的前一处理结点值
                    pre_map.put("pre",root.val);
                }
            }
            //处理弹出结点的右儿子
            root=root.right;
            if(root!=null){
                nodes.push(root);
            }
        }
        
        return true;
    }

    如果明确给出一棵搜索二叉树的根节点root,现在这棵树中两个结点交换了位置使得该树不是搜索二叉树了(递增性被破坏)。求出这两个错位的结点?

    我们知道,搜索二叉树的中序遍历序列是有序的递增序列,比如:1 2 3 4 5 6 7

    现在假设两结点交换了位置,使得中序遍历结果为:1 2 6 4 5 3 7。我们可以发现,3与6所在结点发生了交换,使得序列不是单纯递增,而是出现了两个逆序对:6 45 3。并且交换位置的两数6、3分别是两逆序对的 较大者  与  较小者。

    所以,从出错的搜索二叉树中找到交换了位置的两个结点的简单思路就是:中序遍历这棵二叉树,在遍历过程中找出逆序对(后一元素大于前一元素):

                                                                                如果只有一个逆序对,则发生交换的结点就是这个逆序对的两个元素;

                                                                                如果有两个逆序对,那么发生交换的两个结点就是:第一个逆序对的较大者  与  第二个逆序对的较小者

    (注意:因为只有两个出错结点,所以无论是一个逆序对还是两个逆序对:我们默认只有一个逆序对,找到后存到数组中,当有第二个逆序对出现时,只需把数组中第二个元素改成第二逆序对中的小者就行了。)

 public int[] findError(TreeNode root) {
        int[] res=new int[2];
        TreeNode[] errornodes=new TreeNode[2];
        Stack<TreeNode> stack=new Stack<TreeNode>();
        stack.push(root);
        TreeNode prenode=null;
        while(!stack.isEmpty()){
            while(root!=null){
                root=root.left;
                if(root!=null){
                    stack.push(root);
                }
            }
            
            root=stack.pop();
            
            if(prenode!=null && prenode.val>root.val){
                //第一次出现逆序对时,存在了errornodes[]中。如果有第二个逆序对的出现,那么第二个逆序对的小者(后者)为错误结点,前者不用保存
                errornodes[0]=(errornodes[0]==null?prenode:errornodes[0]);
                errornodes[1]=root;
            }
            //修改前继结点,作为下一出栈的结点的前继结点
            prenode=root;
            
            root=root.right;
            if(root!=null){
                stack.push(root);
            }          
        }
        //找到出错的结点后,输出结点值时它们交换回来再输出!
        res[0]=errornodes[1].val;
        res[1]=errornodes[0].val;
        return res;
    }
原文地址:https://www.cnblogs.com/ygj0930/p/6611552.html