当所有的静态查找结构添加和删除一个数据的时候,整个结构都需要重建。这对于常常需要在查找过程中动态改变数据而言,是灾难性的。因此人们就必须去寻找高效的动态查找结构,我们在这讨论一个非常常用的动态查找树——二叉查找树 

二叉查找树的特点

下面的图就是两棵二叉查找树,我们可以总结一下他的特点:

(1) 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值

(2) 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值
(3) 它的左、右子树也分别为二叉查找树

我们中序遍历这两棵树发现一个有序的数据序列: 【1  2  3  4  5  6  7  8 】

二叉查找树的操作

插入操作:

现在我们要查找一个数9,如果不存在则,添加进a图。我们看看二叉查找树动态添加的过程:

1). 数9和根节点4比较(9>4),则9放在节点4的右子树中。

2). 接着,9和节点5比较(9>5),则9放在节点5的右子树中。

3). 依次类推:直到9和节点8比较(9>8),则9放在节点8的右子树中,成为节点8的右孩子。

这个过程我们能够发现,动态添加任何一个数据,都会加在原树结构的叶子节点上,而不会重新建树。 由此可见,动态查找结构确实在这方面有巨大的优势。

删除操作:

如果二叉查找树中需要删除的结点左、右子树都存在,则删除的时候需要改变一些子树结构,但所需要付出的代价很小。

具体的插入,删除算法请参加《数据结构算法与应用——搜索树》P5-8。[该章节已经上传到《查找结构专题(6):动态查找树比较 》中]。

二叉查找树的效率分析

那么我们再来看看二叉查找树的效率问题

很显然,在a,b两图的二叉查找树结构中查找一个数据,并不需要遍历全部的节点元素,查找效率确实提高了。但是有一个很严重的问题:我们在a图中查找8需要比较5次数据,而在B图中只需要比较3次。更为严重的是:如果按有序序列[1 2 3 4 5 6 7 8]建立一颗二叉查找树,整棵树就退化成了一个线性结构(如c输入图:单支树),此时查找8需要比较8次数据,和顺序查找没有什么不同。

总结一下:最坏情况下,构成的二叉排序树蜕变为单支树,树的深度为n,其查找时间复杂度与顺序查找一样O(N)。最好的情况是二叉排序树的形态和折半查找的判定树相同,其平均查找长度和log2(N)成正比 (O(log2(n)))。

这说明:同样一组数据集合,不同的添加顺序会导致查找树的结构完全不一样,直接影响了查找效率。

那么如何解决这个问题呢? 我们会在下面的专题中:《平衡二叉树》 中来解决。

package net.hr.algorithm.search;

import java.util.ArrayList;

/**
 * 二叉树节点结构
 * @author heartraid
 */
class BSTNode<E extends Comparable<E>>{
    /**结点关键字*/
    E key=null;
    /**直接父亲结点*/
    BSTNode<E> parent=null;
    /**结点左子树的根节点*/
    BSTNode<E> lchild=null;
    /**结点右子树的根节点*/
    BSTNode<E> rchild=null;

    BSTNode(E k){
        this.key=k;
    }

}
/**
 * 二叉查找树 Binary Search Tree(BST)
 * @author heartraid
 *
 */
public class BST<E extends Comparable<E>> {
    /**树根*/
    private BSTNode<E> root=null;

    public BST(){
    }

    /**
     * BST 查询关键字
     * @param key 关键字
     * @return 查询成功/true, 查询失败/false
     */
    public boolean search(E key){
        System.out.print("搜索关键字["+key+"]:");
        if(key==null||root==null){
            System.out.println("搜索失败");
            return false;
        }
        else{
            System.out.print("搜索路径[");
            if(searchBST(root,key)==null){
                return false;
            }
            else return true;

        }
    }
    /**
     * BST插入关键字
     * @param key 关键字
     * @return 插入成功/true, 插入失败/false
     */
    public boolean insert(E key){
        System.out.print("插入关键字["+key+"]:");
        if(key==null) return false;
        if(root==null){
            System.out.println("插入到树根。");
            root=new BSTNode<E>(key);
            return true;
        }
        else{
            System.out.print("搜索路径[");
            return insertBST(root,key);
        }
    }

    public boolean delete(E key){
        System.out.print("删除关键字["+key+"]:");
        if(key==null||root==null){
            System.out.println("删除失败");
            return false;
        }
        else{
            System.out.print("搜索路径[");

            //定位到树中待删除的结点
            BSTNode<E> nodeDel=searchBST(root,key);
            if(nodeDel==null){
                return false;
            }
            else{
                //nodeDel的右子树为空,则只需要重接它的左子树
                if(nodeDel.rchild==null){

                    BSTNode<E> parent=nodeDel.parent;
                    if(parent.lchild.key.compareTo(nodeDel.key)==0)
                        parent.lchild=nodeDel.lchild;
                    else
                        parent.rchild=nodeDel.lchild;
                }
                //左子树为空,则重接它的右子树
                else if(nodeDel.lchild==null){
                    BSTNode<E> parent=nodeDel.parent;
                    if(parent.lchild.key.compareTo(nodeDel.key)==0)
                        parent.lchild=nodeDel.rchild;
                    else
                        parent.rchild=nodeDel.rchild;
                }
                //左右子树均不空
                else{
                    BSTNode<E> q=nodeDel;
                    //先找nodeDel的左结点s
                    BSTNode<E> s=nodeDel.lchild;
                    //然后再向s的右尽头定位(这个结点将替代nodeDel),其中q一直定位在s的直接父亲结点
                    while(s.rchild!=null){
                        q=s;
                        s=s.rchild;
                    }
                    //换掉nodeDel的关键字为s的关键字
                    nodeDel.key=s.key;
                    //重新设置s的左子树
                    if(q!=nodeDel)
                        q.rchild=s.lchild;
                    else
                        q.lchild=s.lchild;
                }
                return true;
            }
        }
    }

    /**
     * 递归查找关键子
     * @param node 树结点
     * @param key 关键字
     * @return 查找成功,返回该结点,否则返回null。
     */
    private BSTNode<E> searchBST(BSTNode<E> node, E key){
        if(node==null){
            System.out.println("].  搜索失败");
            return null;
        }
        System.out.print(node.key+" —>");
        //搜索到关键字
        if(node.key.compareTo(key)==0){
            System.out.println("].  搜索成功");
            return node;
        }
        //在左子树搜索
        else if(node.key.compareTo(key)>0){
                return searchBST(node.lchild,key);
        }
        //在右子树搜索
        else{
            return searchBST(node.rchild,key);
        }
    }

    /**
     * 递归插入关键字
     * @param node 树结点
     * @param key 树关键字
     * @return true/插入成功,false/插入失败
     */
    private boolean insertBST(BSTNode<E> node, E key){
        System.out.print(node.key+" —>");
        //在原树中找到相同的关键字,无需插入。
        if(node.key.compareTo(key)==0)
        {
            System.out.println("].  搜索有相同关键字,插入失败");
            return false;
        }
        else{
            //搜索node的左子树
            if(node.key.compareTo(key)>0){
                //如果当前node的左子树为空,则将新结点key node插入到左孩子处
                if(node.lchild==null) {
                    System.out.println("].  插入到"+node.key+"的左孩子");
                    BSTNode<E> newNode=new BSTNode<E>(key);
                    node.lchild=newNode;
                    newNode.parent=node;
                    return true;
                }
                //如果当前node的左子树存在,则继续递归左子树
                else return insertBST(node.lchild, key);
            }
            //搜索node的右子树
            else{
                if(node.rchild==null){
                    System.out.println("].  插入到"+node.key+"的右孩子");
                    BSTNode<E> newNode=new BSTNode<E>(key);
                    node.rchild=newNode;
                    newNode.parent=node;
                    return true;
                }
                else return insertBST(node.rchild,key);
            }
        }

    }
    /**
     * 得到BST根节点
     * @return BST根节点f
     */
    public BSTNode<E> getRoot(){
        return this.root;
    }
    /**
     * 非递归中序遍历BST
     */
    public void InOrderTraverse(){
        if(root==null)
            return;
        BSTNode<E> node=root;
        ArrayList<BSTNode<E>> stack=new ArrayList<BSTNode<E>>();
        stack.add(node);
        while(!stack.isEmpty()){
            while(node.lchild!=null){
                node=node.lchild;
                stack.add(node);
            }
            if(!stack.isEmpty()){
                BSTNode<E> topNode=stack.get(stack.size()-1);
                System.out.print(topNode.key+" ");
                stack.remove(stack.size()-1);
                if(topNode.rchild!=null){
                    node=topNode.rchild;
                    stack.add(node);
                }
            }
        }

    }

    /**
     * 测试
     */
    public static void main(String[] args) {
        BST<Integer> tree=new BST<Integer>();
        tree.insert(new Integer(100));
        tree.insert(new Integer(52));
        tree.insert(new Integer(166));
        tree.insert(new Integer(74));
        tree.insert(new Integer(11));
        tree.insert(new Integer(13));
        tree.insert(new Integer(66));
        tree.insert(new Integer(121));

                tree.search(new Integer(11));
                tree.InOrderTraverse();

                tree.delete(new Integer(11));
        tree.InOrderTraverse();

    }

}

【查找结构 2】二叉查找树 [BST]的更多相关文章

  1. 【查找结构3】平衡二叉查找树 [AVL]

    在上一个专题中,我们在谈论二叉查找树的效率的时候.不同结构的二叉查找树,查找效率有很大的不同(单支树结构的查找效率退化成了顺序查找).如何解决这个问题呢?关键在于如何最大限度的减小树的深度.正是基于这 ...

  2. 查找系列合集-二叉查找树BST

    一. 二叉树 1. 什么是二叉树? 在计算机科学中,二叉树是每个结点最多有两个子树的树结构. 通常子树被称作“左子树”(left subtree)和“右子树”(right subtree). 二叉树常 ...

  3. [学习笔记] 二叉查找树/BST

    平衡树前传之BST 二叉查找树(\(BST\)),是一个类似于堆的数据结构, 并且,它也是平衡树的基础. 因此,让我们来了解一下二叉查找树吧. (其实本篇是作为放在平衡树前的前置知识的,但为了避免重复 ...

  4. 二叉查找树(BST)

    二叉查找树(BST):使用中序遍历可以得到一个有序的序列

  5. 二叉查找树BST 模板

    二叉查找树BST 就是二叉搜索树 二叉排序树. 就是满足 左儿子<父节点<右儿子 的一颗树,插入和查询复杂度最好情况都是logN的,写起来很简单.   根据BST的性质可以很好的解决这些东 ...

  6. 【查找结构5】多路查找树/B~树/B+树

    在前面专题中讲的BST.AVL.RBT都是典型的二叉查找树结构,其查找的时间复杂度与树高相关.那么降低树高自然对查找效率是有所帮助的.另外还有一个比较实际的问题:就是大量数据存储中,实现查询这样一个实 ...

  7. 二叉查找树(BST)的实现

    一.二叉树介绍 二叉查找树(Binary Search Tree,BST),又称二叉排序树,也称二叉搜索树,它或者是一颗空树,或者具有如下性质的树:若它的左子树不为空,则左子树上所有节点的值都小于根节 ...

  8. 3.2 符号表之二叉查找树BST

    一.插入和查找 1.二叉查找树(Binary Search Tree)是一棵二叉树,并且每个结点都含有一个Comparable的键,保证每个结点的键都大于其左子树中任意结点的键而小于其右子树的任意结点 ...

  9. 【查找结构4】红黑树 [RBT]

    红黑树的性质与定义 红黑树(red-black tree) 是一棵满足下述性质的二叉查找树: 1. 每一个结点要么是红色,要么是黑色. 2. 根结点是黑色的. 3. 所有叶子结点都是黑色的(实际上都是 ...

随机推荐

  1. android开发 textview根据字数长度自动调整字体大小

    需求:根据输入的值实时显示到textview中预览,但是字体大小是要自动适配的 网上有一个代码,但是在我这里不能用,注意方法:refitText  注释掉的是之前的代码 import com.cars ...

  2. JavaScript原型与原型链学习笔记

    一.什么是原型?原型是一个对象,其他对象可以通过它实现属性继承.简单的说就是任何一个对象都可以成为原型 prototype属性: 我们创建的每个函数都有一个prototype属性,这个属性是一个指针, ...

  3. .NET Framework 4.5、4.5.1 和 4.5.2 中的新增功能

    .NET Framework 4.5.4.5.1 和 4.5.2 中的新增功能 https://msdn.microsoft.com/zh-cn/library/ms171868.aspx

  4. ansii、unicode、utf8 区别和关系

    本地化过程中涉及到源文件和目标文件的传输问题,这时候编码就显得很重要.中文的网页和操作系统中通常采用ANSI编码,这也是微软OS的一个字符标准.对于ANSI,不同的国家和地区制定了不同的标准,由此产生 ...

  5. 图解Git/图形化的Git参考手册

    此页图解git中的最常用命令.如果你稍微理解git的工作原理,这篇文章能够让你理解的更透彻. 基本用法 上面的四条命令在工作目录.暂存目录(也叫做索引)和仓库之间复制文件. ● git add fil ...

  6. bzoj 2697 贪心

    就贪心就行了,首先可以看成n个格子,放物品,那么 一个物品假设放3个,放在1,k,n处,那么价值和放在1,n 是一样的,所以一个物品只放两个就行了,价值大的应该尽量放 在两边,那么排序之后模拟就行了 ...

  7. BZOJ 3511 土地划分

    AC通道:http://www.lydsy.com/JudgeOnline/problem.php?id=3511 题目分析: 看上去和前面的人员雇佣以及小M种田都很像. 最小割模型来求最大值,一般都 ...

  8. 【POJ】【2699】The Maximum Number of Strong Kings

    网络流/最大流/二分or贪心 题目大意:有n个队伍,两两之间有一场比赛,胜者得分+1,负者得分+0,问最多有几只队伍打败了所有得分比他高的队伍? 可以想到如果存在这样的“strong king”那么一 ...

  9. linux系统非ROOT用户80端口不能启动tomcat问题的变通办法——通过Iptables端口转发

    2010-07-17 13:21:42 org.apache.tomcat.util.digester.SetPropertiesRule begin 警告: [SetPropertiesRule]{ ...

  10. ie 与 Chrome 时间格式化问题.

    ie 与 Chrome 时间格式化通用: new Date(res[i].Time.replaceAll("-", "/")).format("yyy ...