zoukankan      html  css  js  c++  java
  • 二叉树进阶之搜索二叉树的判断与找出搜索二叉树中出错的结点

    转载请注明原文地址: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;
        }
  • 相关阅读:
    静态与动态数据分析的测试方法
    网址无法访问,显示服务器拒绝了请求
    stf浏览器端删除离线设备
    STF日志提示 Not found ; no service started
    通过adb命令获取apk的安装路径
    js页面打开方式
    windows和mac回退及撤销快捷键
    单元测试规范
    Git :fatal: refusing to merge unrelated histories解决
    mysql详解4:分组
  • 原文地址:https://www.cnblogs.com/ygj0930/p/6611552.html
Copyright © 2011-2022 走看看