数据结构-二叉搜索树

二叉搜索树

二叉搜索树又称为二叉排序树,它要么是一颗空树,要么是一颗具有以下性质的树:

  • 若他的左子树不为空,则左子树上所有节点的值都小于根结点的值
  • 若他的右子树不为空,则右子树上所有节点的值都大于根节点的值
  • 它的左右子树也满足二叉搜索树的性质

如下图所示,满足一个二叉搜索树:

这里写图片描述

对一个二叉搜索树做以下操作:

  • 插入
  • 查找
  • 删除

插入

向一个二叉搜索树中插入一个元素:
需要考虑两种情况:向空树中插入一个元素以及向非空树中插入一个元素。
这里写图片描述
向非空树中插入元素需要考虑他将要插入的位置在哪里,必须保证插入之后还是要满足一个二叉搜索树的性质。

这里写图片描述
以下是采取递归方法实现的代码:

void SearchTreeInsert(SearchTreeNode** root, SearchTreeType key)
{
    if(root==NULL)
    {
        return;
    }
    //空树的情况
    if(*root==NULL)
    {
        SearchTreeNode* new_node=CreateNode(key);
        *root=new_node;
        return;
    }
    SearchTreeNode* to_find=*root;
    if(key>to_find->key)
    {
        SearchTreeInsert(&to_find->rchild,key);
    }
    else if(key<to_find->key)
    {
        SearchTreeInsert(&to_find->lchild,key);
    }
    else
    {
        return;//默认不允许出现相同元素
    }
}

以下是采取非递归方式的代码:

void SearchTreeInsertByLoop(SearchTreeNode** root, SearchTreeType key)
{
    if(root==NULL)
    {
        return;
    }
    if(*root==NULL)
    {
        SearchTreeNode* new_node=CreateNode(key);
        *root=new_node;
    }
    else
    {
        //先找到要插入的位置 
        //cur表示要插入的位置
        //pre表示要插入的位置的父结点
        SearchTreeNode* cur=*root;
        SearchTreeNode* pre=NULL;
        while(cur)
        {
            if(cur->key>key)
            {
                pre=cur;
                cur=cur->lchild;
            }
            else if(cur->key<key)
            {
                pre=cur;
                cur=cur->rchild;
            }
            else
            {
                break;
            }
        }
        //此时已经可以插入了
        if(pre->key>key)
        {
            pre->lchild=CreateNode(key);
        }
        else
        {
            pre->rchild=CreateNode(key);
        }
    }
}

查找

查找也是根据该二叉搜索树的性质进行的查找操作。首先与根节点比较,大于根节点就去它的右子树查找,小于根节点,就去它的左子树查找。
递归实现:

SearchTreeNode* SearchTreeFind(SearchTreeNode* root, SearchTreeType to_find)
{
    if(root==NULL)
    {
        return NULL;
    }
    if(to_find>root->key)
    {
        return SearchTreeFind(root->rchild,to_find);
    }
    else if(to_find<root->key)
    {
        return SearchTreeFind(root->lchild,to_find);
    } 
    else
    {
        return root;
    }
}
SearchTreeNode* SearchTreeFindByLoop(SearchTreeNode* root, SearchTreeType to_find)
{
    if(root==NULL)
    {
        return NULL;
    }
    SearchTreeNode* cur=root;
    while(cur!=NULL)
    {
        if(cur->key>to_find)
        {
            cur=cur->lchild;
        }
        else if(cur->key<to_find)
        {
            cur=cur->rchild;
        }
        else
        {
            break;
        }
    }
    return cur;
}

删除

删除一个二叉搜索树中元素实现起来需要考虑以下几种情况:

  • 要删除的元素查找得到:
    • 删除的元素无左右子树
    • 删除的元素只有左子树
    • 删除的元素只有右子树
    • 删除的元素具有左右子树
  • 要删除的元素查找不到

要删除的元素无左右子树:
这里写图片描述
要删除的元素只有左子树(右子树相同)
这里写图片描述
要删除的元素具有左右子树:

void SearchTreeRemoveByLoop(SearchTreeNode** root, SearchTreeType key)
{
    if(root==NULL)
    {
        return;
    }
    if(*root==NULL)
    {
        return;
    }
    SearchTreeNode* to_remove=*root;
    SearchTreeNode* parent=NULL;
    //先找到删除的位置
    while(to_remove!=NULL)
    {
        if(to_remove->key>key)
        {
            parent=to_remove;
            to_remove=to_remove->lchild;
        }
        else if(to_remove->key<key)
        {
            parent=to_remove;
            to_remove=to_remove->rchild;
        }
        else
        {
            break;
        }
    }
    //找不到的情况
    if(to_remove==NULL)
    {
        return;
    }
    //此时已经找到位置
    //分情况讨论
    //1.无左右子树
    if(to_remove->lchild==NULL&&to_remove->rchild==NULL)
    {
        if(parent->key>key)
        {
            parent->lchild=NULL;
            free(to_remove);
        }
        else
        {
            parent->rchild=NULL;
            free(to_remove);
        }
    }

    //2.只有左子树
    else if(to_remove->lchild!=NULL&&to_remove->rchild==NULL)
    {
        parent->lchild=to_remove->lchild;
        free(to_remove);
    }
    //3.只有右子树
    else if(to_remove->lchild==NULL&&to_remove->rchild != NULL)
    {
        parent->rchild=to_remove->rchild;
        free(to_remove);
    }

    //4.有左右子树
    else
    {
        SearchTreeNode* min=to_remove->rchild;
        while(min->lchild!=NULL)
        {
            parent=min;
            min=min->lchild;
        }
        //此时找到了最小的结点
        to_remove->key=min->key;
        parent->lchild=min->rchild;
        free(min);
    }
}

void SearchTreeRemove(SearchTreeNode** root, SearchTreeType key)
{
    if(root==NULL)
    {
        return;
    }
    if(*root==NULL)
    {
        return;
    }
    SearchTreeNode* cur=*root;
    if(key>cur->key)
    {
        return SearchTreeRemove(&cur->rchild,key);
    }
    else if(key<cur->key)
    {
        return SearchTreeRemove(&cur->lchild,key);
    } 
    else
    {
        //找得到删除的位置
        //1.无左右子树
        if(cur->lchild==NULL&&cur->rchild==NULL)
        {
            *root=NULL;
            free(cur);
            return;
        }
        //2.有左子树
        else if(cur->lchild!=NULL&&cur->rchild==NULL)
        {
            *root=cur->lchild;
            free(cur);
            return;
        }
        //3.有右子树
        else if(cur->lchild==NULL&&cur->rchild!=NULL)
        {
            *root=cur->rchild;
            free(cur);
            return;
        }
        //4.既有左子树又有右子树
        else if(cur->lchild!=NULL&&cur->rchild!=NULL)
        {
            SearchTreeNode* min=cur->rchild;
            while(min->lchild!=NULL)
            {
                min=min->lchild;
            }
            cur->key=min->key;
            SearchTreeRemove(&cur->rchild,min->key);
            return;
        }
    }
    return;
}

二叉搜索树的性能

二叉搜索树不管是插入还是删除,都要先进行查找,查找的效率代表了二叉搜索树中各个操作的性能。
最优情况下,二叉搜索树为完全二叉树,时间复杂度:O(logN)
最坏情况下,二叉搜索树退化为单支树,时间复杂度:O(N)

因此,当二叉搜索树退化为单支树时,效率非常底下,查找元素相当于在顺序表中搜索元素,效率低下。

AVL

一颗AVL树或者是空树,或者是具有以下性质的二叉搜索树:左右子树都是AVL树。
并且左右子树的高度之差绝对值不超过1
对于一颗AVL树,平均搜索时间复杂度是:O(lgN)

红黑树

红黑树是一颗二叉搜索树,它在每个结点上增加了一个存储位来表示节点的颜色,可以是red或是black,通过对任何一条从根结点到叶子节点简单路径上的颜色来约束,红黑树可保证最长路径不超过最短路径的两倍,因而近似平衡。所以实际上,红黑树的性能比AVL树性能高。

猜你喜欢

转载自blog.csdn.net/mxrrr_sunshine/article/details/80328984
今日推荐