关闭

查找算法总结(3)--二叉查找树

标签: 算法
57人阅读 评论(0) 收藏 举报
分类:

转自http://hxraid.iteye.com/blog/609312

一、简介

当所有的静态查找结构添加和删除一个数据的时候,整个结构都需要重建。这对于常常需要在查找过程中动态改变数据而言,是灾难性的。因此人们就必须去寻找高效的动态查找结构,我们在这讨论一个非常常用的动态查找树——二叉查找树 。
二叉查找树的特点:
(1) 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值
(2) 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值
(3) 它的左、右子树也分别为二叉查找树
这里写图片描述
我们中序遍历这两棵树发现一个有序的数据序列: 【1 2 3 4 5 6 7 8 】

二、二叉查找树的操作

1、查找操作
在a图中查找关键字6
1)如果数为空,查找失败
2)比较6和根节点,如果相等,查找成功,如果6大于根节点,转向节点的右子树,如果小于6,转向节点的左子树;
3)递归查找每一个节点,直到找到和6相等的节点,否则查找失败。

2、插入操作:
现在我们要查找一个数9,如果不存在则,添加进a图。我们看看二叉查找树动态添加的过程:
1). 数9和根节点4比较(9>4),则9放在节点4的右子树中。
2). 接着,9和节点5比较(9>5),则9放在节点5的右子树中。
3). 依次类推:直到9和节点8比较(9>8),则9放在节点8的右子树中,成为节点8的右孩子。
这个过程我们能够发现,动态添加任何一个数据,都会加在原树结构的叶子节点上,而不会重新建树。 由此可见,动态查找结构确实在这方面有巨大的优势。

3、删除操作:
删除操作分为3中情况,根据不同的情况有不同的操作:
1)需要删除的节点是叶节点,直接删除
2)需要删除的节点只有一个左子树或右子树,用子树代替该点
3)需要删除的节点左、右子树都存在。

三、二叉查找树的实现

package binarytree;
/*
 * 二叉树查找树的表示和操作
 * 1、构建二叉树查找树
 * 2、二叉树查找树的查找,包括任意一个值,最大值,最小值,任意值的前驱和后继
 * 3、二叉树查找树的插入和删除
 * */
public class BSTDemo {
    private BSTDemo tree;
    private Node root;


    public BSTDemo(){}

    public Node getRoot(){
        return root;
    }

    //查找,递归
    public int find(Node node,int value){
        if(node==null) {
            System.out.println("查找失败");
            return value;
        }
        if(node.getKey()==value){
            return value;
        }
        if(node.getKey()>value){
            return find(node.getLChild(),value);
        }
        if(node.getKey()<value){
            return find(node.getRChild(),value);
        }

        return value;
    }

    //查找,非递归
    public int loopFind(Node node,int value){
        while(node!=null){
            if(node.getKey()==value){
                return value;
            }
            if(node.getKey()>value){
                node=node.getLChild();
            }
            else{
                node=node.getRChild();
            }
        }
        System.out.println("查找失败");
        return value;
    }

    //查找最大值
    public Node getMin(Node node){
        while(node.getLChild()!=null){
            node=node.getLChild();
        }
        return node;
    }

    //查找最小值
    public Node getMax(Node node){
        while(node.getRChild()!=null){
            node=node.getRChild();
        }
        return node;
    }

    //获取给定点的前驱
    public Node getPredecessor(Node node){
        //如果左子树不空,前驱是左子树的最大值
        if(node.getLChild()!=null){
            return getMax(node.getLChild());
        }
        //如果左子树为空
        Node p=node.getParent();
        Node x;
        while(p!=null && node==p.getLChild()){
            x=p;
            p=p.getParent();
        }
        return p;
    }
    //获取给顶点的后继
    public Node getSuccessor(Node node){
        //如果右子树不空,后继是右子树的最小值
        if(node.getRChild()!=null){
            return getMin(node.getRChild());
        }
        //如果右子树空
        Node p=node.getParent();
        Node x;
        while(p!=null && node==p.getRChild()){
            x=p;
            p=p.getParent();
        }
        return p;
    }

    //插入,非递归
    public void insert(int value){
        //如果树根为空,则树为空,插入的元素作为根
        Node temp=new Node(value);
        if(this.root==null){
            this.root=temp;
            return;
        }
        //如果树不为空,则查找插入合适的位置
        Node node=root;
        while(node!=null){
            if(node.getKey()==value){
                node.setKey(value);
            }
            if(node.getKey()>value){
                if(node.getLChild()==null){
                    node.setLChild(temp);
                    temp.setParent(node);
                    return;
                }else node=node.getLChild();
            }
            else{
                if(node.getRChild()==null){
                    node.setRChild(temp);
                    temp.setParent(node);
                    return;
                }else node=node.getRChild();
            }
        }
    }

    public void insertAll(int[] array){
        for(int x:array){
            insert(x);
        }
    }

    //给定一个节点,并删除
    public void remove(Node node){
        if(node==null) return;
        if(node.getLChild()==null){
            //左子树为空,用右子树代替
            trans(node,node.getRChild());
        }else if(node.getRChild()==null){
            //如果右子树空,用左子树代替
            trans(node,node.getLChild());
        }else{
            //如果都不空,首先找到该点的直接后继
            Node suc=getSuccessor(node);
            if(suc.getParent()!=node){
                //如果直接后继的父节点不是node,因为suc没有左子树,所有用右子树代替
                trans(suc,suc.getRChild());
                suc.setRChild(node.getRChild());
                suc.getRChild().setParent(suc);
            }
            //如果直接后继是node的右子树,因为suc没有左子树,所以用suc直接代替node
            trans(node,suc);
            suc.setLChild(node.getLChild());
            suc.getLChild().setParent(node);

        }
    }

    //用y替换x
    public void trans(Node x,Node y){
        if (x.getParent()==null){
            //如果是根节点
            this.root=y;
        }else if(x==x.getParent().getLChild()){
            //如果是左子树
            x.getParent().setLChild(y);
        }else x.getParent().setRChild(y);

        if(y==null){
            //如果y是空
            x.setParent(null);
        }
    }
//判断一个二叉树是否为二叉查找树
public boolean isSorted(Node node){
    if(node==null) return true;
    return (node.getLChild()==null || node.getLChild()!=null && node.getKey()>node.getLChild().getKey()&& node.getRChild()==null || node.getRChild()!=null && node.getKey()<node.getRChild().getKey()
&& isSorted(node.getLChild()) && isSorted(node.getRChild()));
    }
}

四、性能分析

二叉查找树的所有操作的运行时间和树的形状有关,树的形状又和插入元素的顺序有关。在最好的情况下,节点完全平衡,从根节点到最底层叶子节点只有lgN个节点。在最差的情况下,根节点到最底层叶子节点会有N各节点。在一般情况下,树的形状和最好的情况接近。

0
0

猜你在找
C语言及程序设计(讲师:贺利坚)
Python爬虫工程师培养课程全套(讲师:韦玮)
Python全栈开发入门与实战课(讲师:李杰)
2017软考网络规划设计师视频套餐(讲师:任铄)
2017软考软件设计师视频套餐(讲师:任铄)
2017软考-信息系统项目管理师视频套餐(讲师:任铄)
软考(高级)项目经理实战营(讲师:张传波)
微信公众平台开发套餐(讲师:刘运强)
深度学习原理+实战+算法+主流框架套餐(讲师:唐宇迪)
2017系统集成项目管理工程师通关套餐(讲师:徐朋)
查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:2100次
    • 积分:154
    • 等级:
    • 排名:千里之外
    • 原创:14篇
    • 转载:6篇
    • 译文:0篇
    • 评论:0条
    文章分类
    文章存档