zoukankan      html  css  js  c++  java
  • 17.二叉排序树的查找、插入、删除操作

    /*
    8.6 二叉排序树:在创建树的时候就构建一个有序的树
    特点:
    1.若它的左子树不空,则左子树上所有结点的值均小于它的根结构的值;
    2.若它的右子树不空,则右子树上所有结点的值均大于它的根节点的值;
    3.它的左、右子树也分别为二叉排序树
    
    构建一颗二叉排序树的目的,其实并不是为了排序,而是为了提高查找和插入删除关键字的速度。不管怎么说,在一个有序数据集上的查找,
    速度总是要快于无序的数据集的,而二叉排序树这种非线性的结构,也有利于插入和删除的实现。
    */
    /*
    8.6.1 二叉排序树查找操作
    首先我们提供一个二叉树结构
    */
    typedef struct BiTNode
    {
        //结点数据
        int data;
        //左右孩子指针
        struct BiTNode *lchild, *rchild;
    } BiTNode, *BiTree;
    //然后我们来看看二叉排序树的查找是如何实现的
    
    /*
    递归查找二叉排序树T中是否存在key
    指针f指向T的双亲,其初始调用值为NULL
    若查找成功,则指针p指向该数据元素结点,并返回TRUE
    否则指针p指向查找路径上访问的最后一个结点并返回FALSE
    */
    Status SearchBST(BiTree T, int key, BiTree f, BiTree *p)
    {
        //查找不成功
        if(!T)
        {
            *p = f;
            return FALSE;
        }
        //查找成功
        else if(key == T->data)
        {
            *p = T;
            return TRUE;
        }
        else if (key < T->data)
            return SearchBST(T->lchild, key, T, p);     //在左子树继续查找
        else
            return SearchBST(T->rchild, key, T, p);    //在右子树继续查找
    }
    
    /*8.6.2 二叉排序树插入操作
    有了二叉排序树的查找函数,那么所谓的二叉排序树的插入,其实也就是将关键字放到树中合适位置而已。
    
    当二叉排序树T中不存在关键字等于key的数据元素时,插入key并返回TRUE,否则返回FALSE
    */
    Status InsertBST(BiTree *T, int key)
    {
        BiTree p,s;
        //查找不成功
        if(!SearchBST(*T, key, NULL, &p))
        {
            s = (BiTree)malloc(sizeof(BiTNode));
            s->data = key;
            s->lchild = s->rchild = NULL;
            if(!p)
                //插入s为新的根节点
                *T = s;
            else if(key < p->data)
                //插入s为左孩子
                p->lchild = s;
            else
                //插入孩子为右孩子
                p->rchild = s;
            return TRUE;
        }
        else
            //树中已有关键字相同的结点,不再插入
            return FALSE;    
    }
    
    /*
    8.6.3 二叉树删除操作
    俗话说“请神容易送神难”,我们已经介绍了二叉排序树的查找与插入算法,但是对于二叉排序树的删除,就不那么容易,
    我们不能因为删除了结点,而让这棵树变得不满足二叉树排序的特性,所以删除需要考虑多种情况。
    */
    
    /*
    根据我们对删除结点三种情况的分析:
    1.叶子结点;
    2.仅有左或右子树的结点;
    3.左右子树都有结点,我们来看代码,下面这个算法是递归方式对二叉排序树T查找key,查找到时删除。
    */
    /*
    若二叉排序树T中存在关键字等于key的数据元素时,则删除该数据元素结点,并返回TRUE;否则返回FALSE
    */
    Status DeleteBST(BiTree *T, int key)
    {
        //不存在关键字等于key的数据元素
        if(!*T)
            return FALSE;
        else
        {
            //找到关键字等于key的数据元素
            if (key == (*T)->data)
                return Delete(T);
            else if (key < (*T)->data)
                return DeleteBST(&(*T)->lchild, key);
            else
                return DeleteBST(&(*T)->rchild, key);
        }
    }
    /*
    上边这段代码和前面的二叉排序树查找几乎完全相同,唯一的区别就在于第8行,此时执行的时Delete方法,
    对当前结点进行三处操作。我们来看Delete的代码。
    */
    //从二叉排序树中删除结点p,并重接它的左或右子树。
    Status Delete(BiTree *p)
    {
        BiTree q,s;
        //右子树空则只需重接它的左子树
        if ((*p)->rchild == NULL)
        {
            q = *p;
            *p = (*p)->lchild;
            free(q);
        }
        //左子树为空则只需重接它的右子树
        if((*p)->lchild == NULL)
        {
            q = *p;
            *p = (*p)->rchild;
            free(q);
        }
        //左右子树均不空
        else
        {
            q = *p; s = (*p)->lchild;
            //转左,然后向右到尽头(找待删结点的前驱)
            while (s->rchild)
            {
                q = s; s = s->rchild;
            }
            //s指向被删除结点的直接前驱
            (*p)->data = s->data;
            if (q != *p)
                //重接q的右子树
                q->rchild = s->lchild;
            else
                //重接q的左子树
                q->lchild = s->lchild;
            free(s);
        }
        return TRUE;
    }
    
    /*
    8.6.4 二叉排序树总结
    总之,二叉排序树是以链接的方式存储,保持了链接存储结构在执行插入或删除操作时不用移动元素的优点,
    只要找到合适的插入和删除位置后,仅需修改链接指针即可。插入删除的时间性能比较好。而对于二叉排序树
    的查找,走的就是从根节点到要查找的结点的路径,其比较次数等于给定值的结点在二叉排序树的层数。极端
    情况,最少为1次,即根节点就是要找的结点,最多也不会超过树的深度。也就是说,二叉排序树的查找性能
    取决于二叉树的形状。可问题就在于,二叉排序树的形状是不确定的,有可能时极端的左倾或者右倾。
        也就是说,我们希望二叉排序树是平衡的,即其深度与完全二叉树相同,均为,那么查找的时间复杂度
    也就是O(logn),近似于折半查找,事实上左图也不够平衡(p580 图8-6-18),明显的左重右轻。
        不平衡的最坏情况就是像p580 图8-6-18 右图的斜树,查找时间复杂度为O(n),这等同于顺序查找。
        因此,如果我们希望对一个集合按二叉排序树查找,做好是把它们构建成一颗平衡的二叉排序树。这样我们就
        引申出另一个问题,如何让二叉排序树平衡的问题。
    */
    
  • 相关阅读:
    两路归并算法
    个性化搜索引擎调研(三)
    编程珠玑开篇磁盘文件排序问题
    Lucene里经常被聊到的几个话题
    成就霸业的座右铭(绝对经典)
    别人对你的态度,决定了你的命运
    iBATIS缓存实现分析[转]
    Taste/Thoth:Taste Architecture 概览【转Beyond Search】
    中文分词算法笔记
    ConcurrentModificationException主要原因及处理方法
  • 原文地址:https://www.cnblogs.com/go-ahead-wsg/p/13261493.html
Copyright © 2011-2022 走看看