二叉搜索树的相关操作(递归实现)

本文章主要介绍二叉搜索树的概念以及二叉搜索树的相关操作:按值插入、按值查找、按值删除。

1. 二叉搜索树的概念

二叉搜索树又称为二叉排序树,它或者是一棵空树,或者具有如下性质的二叉树:

(1)若它的左子树不为空,则左子树上的所有节点值都小于根节点的值。

(2)若它的右子树不为空,则右子树上的所有节点值都大于根节点的值。

(3)它的左右子树也为二叉搜索树。

画图举例关于二叉搜索树:


2. 二叉搜索树前期准备工作

二叉搜索树的特点:左子树值小于根节点值,根节点值小于右子树值。

2.1 头文件search_tree.h

扫描二维码关注公众号,回复: 932319 查看本文章

二叉搜索树是以链表的形式组织起来,二叉搜索树节点的结构体类型包括:节点值,节点的左子树的指向、节点的右子树的指向。

//二叉搜索树
#pragma once
//用于测试打印函数名
#define HEADER printf("===========%s=========\n",__FUNCTION__);
//便于代码的修改,重定义二叉搜索树节点的元素类型
typedef char SearchNodeType;
//定义二叉搜索树的节点的结构体类型
typedef struct SearchNode
{
    SearchNodeType data;
    struct SearchNode* lchild;
    struct SearchNode* rchild;
}SearchNode;
//函数声明                                                                                                               
void SearchTreeInit(SearchNode** proot);
void SearchTreeDestroy(SearchNode** proot);
void DestroySearchNode(SearchNode* root);

2.2 初始化

初始化二叉搜索树,即将二叉搜索树初始化为空树,故将指向二叉搜索树的指针置为NULL。

//1.初始化                                                                                                               
//思路:初始化二叉搜索树即二叉搜索树为空树,故将其根节点置为NULL即可
void SearchTreeInit(SearchNode** proot)
{
    //非法输入
    if(proot==NULL)
        return;
    //将根节点置为NULL
    *proot=NULL;
}

2.3 销毁

销毁其实和初始化所做的工作是类似的,即将以有的二叉搜索树置为空树即可。

//2.销毁
//思路:将已有的二叉搜索树置为空树,需要释放创建的每个节点
void SearchTreeDestroy(SearchNode** proot)
{
    //非法输入
    if(proot==NULL)
        return;
    //空树,直接return
    if(*proot==NULL)
        return;
    //非空树
    SearchNode* root=*proot;
    SearchTreeDestroy(&root->lchild);
    SearchTreeDestroy(&root->rchild);                                                                                    
    DestroySearchNode(root);
    *proot=NULL;
}

2.4 创建二叉搜索树的节点

//创建节点
SearchNode* CreateSearchNode(SearchNodeType value)
{
    SearchNode* new_node=(SearchNode*)malloc(sizeof(SearchNode));
    new_node->data=value;
    new_node->lchild=NULL;
    new_node->rchild=NULL;
    return new_node;                                                                                                     
}

2.5 销毁二叉搜索树的节点

//销毁节点
void DestroySearchNode(SearchNode* root)
{
    free(root);
}

3. 二叉搜索树的相关操作(递归)

3.1 按值插入

(1)当为空树时,直接在其根节点处插入即可

(2)当为非空树时,由于二叉搜索树的特点:左子树值<根节点值,根节点值<右子树值。故将分为三种情况:

①当插入元素值<根节点值时,在其左子树中继续寻找合适的位置插入。

②当插入元素值>根节点值时,在其右子树中继续寻找合适的位置插入。

③当插入元素值=根节点值时,我们约定插入失败,即二叉搜索树中没有重复值。

//思路:1.空树时直接插入在其根节点位置 
//     2.非空树时:(1)当插入元素值<当前节点值时,递归插入到当前节点的左子树中
//                 (2)当插入元素值>当前节点值时,递归插入到当前节点的右子树中
//                  (3)当相等时,我们约定为插入失败!
void SearchTreeInsert(SearchNode** proot,SearchNodeType value)
{
    //非法输入
    if(proot==NULL)
    {
        return;
    }
    //1.空树时直接插入在其根节点位置
    if(*proot==NULL)                                                                                                     
    {
        //创建新节点
        SearchNode* new_node=CreateSearchNode(value);
        //插入到根节点
        *proot=new_node;
    }
    //2.非空树时
    SearchNode* cur=*proot;
    //(1)当插入元素值<当前节点的值时,将其递归插入到当前节点的左子树中
    if(value<cur->data)
    {
        SearchTreeInsert(&cur->lchild,value);
    }
    //(2)当插入元素值>当前节点的值时,将其递归插入到当前节点的右子树中
    else if(value>cur->data)
    {
        SearchTreeInsert(&cur->rchild,value);
    }                                                                                                                    
    //(3)当相等时,我们约定插入失败
    else
    {
        return;
    }
}

3.2 按值查找

(1)当为空树时,直接return NULL

(2)当为非空树时,由于二叉搜索树的特点:左子树值<根节点值,根节点值<右子树值。故将分为三种情况:

①当查找元素值<根节点值时,在其左子树中继续查找。

②当查找元素值>根节点值时,在其右子树中继续查找。

③当插入元素值=根节点值时,找到了。

(3)需要注意的是,当遍历完二叉搜索树还没有找到,则表示不存在直接return NULL

//思路:1.空树时直接返回NULL
//      2.非空树时:(1)当查找的值find<当前节点的data时,递归在当前节点的左子树中查找
//                  (2)当查找的值find>当前节点的data时,递归在当前节点的右子树中查找
//                  (3)当查找的值find=当前节点的data时,说明找到了故返回当前节点的指向
//      3.若还没有返回,则表示找不到,直接return NULL
SearchNode* SearchTreeFind(SearchNode* root,SearchNodeType find)
{
    //1.空树时
    if(root==NULL)
    {
        return NULL;
    }
    //2.非空树时
    //(1)当查找的值find<当前节点data时,递归在当前节点的左子树中查找                                                     
    if(find<root->data)
    {
        return SearchTreeFind(root->lchild,find);
    }
    //(2)当查找的值find>当前节点data时,递归在当前节点的右子树中查找
    else if(find>root->data)
    {
        return SearchTreeFind(root->rchild,find);
    
    }
    //(3)相等时表示找到了
    else 
    {
        return root;
    }
    //3.没找到
    return NULL;
}

3.3 按值删除

假设要删除的元素为to_remove,先找到to_remove元素对应的节点remove,分情况讨论如何删除remove节点:

(1)to_remove元素未找到,即remove为NULL时,直接return

(2)remove无子树时,直接将其指向NULL

(3)remove只有左子树时,直接将其指向remove的左子树

(4)remove只有右子树时,直接将其指向remove的右子树

(5)remove有左右子树时,将remove与remove的右子树中的最小值交换,再去尝试删除交换的节点
//思路:假设要删除的元素为to_remove
//1.先找到to_remove元素对应的节点remove
//2.分情况讨论如何删除remove节点:
//(1)to_remove元素未找到,即remove为NULL时,直接return
//(2)remove无子树时,直接将其指向NULL
//(3)remove只有左子树时,直接将其指向remove的左子树
//(4)remove只有右子树时,直接将其指向remove的右子树
//(5)remove有左右子树时,将remove与remove的右子树中的最小值交换,再去尝试删除交换的节点
void SearchTreeRemove(SearchNode** proot,SearchNodeType to_remove)
{
    //非法输入
    if(proot==NULL)
    {
        return;                                                                                                          
    }
    //空树时
    if(*proot==NULL)
    {
        return;
    }
    //非空树
    //1.先找到to_remove元素对应的节点remove
    SearchNode* root=*proot;
    //(1)当to_remove<root->data时,在root的左子树下删除
    if(to_remove<root->data)
    {
        SearchTreeRemove(&root->lchild,to_remove);
        return;
    }
    //(2)当to_remove>root->data时,在root的右子树下删除
    else if(to_remove>root->data)
    {                                                                                                                    
        SearchTreeRemove(&root->rchild,to_remove);
        return;
    }
    //(3)当找到时
    else
    {
        //(1)删除的节点没有子树时,直接将当前节点的指向NULL
        if(root->lchild==NULL&&root->rchild==NULL)
        {
            *proot=NULL;
            //销毁要删除的节点
            DestroySearchNode(root);

        }
        //(2)删除的节点只有左子树时,直接将当前节点指向其左子树
        else if(root->lchild!=NULL&&root->rchild==NULL)
        {
            *proot=root->lchild;
            //销毁要删除的节点                                                                                           
            DestroySearchNode(root);
        }
        //(3)删除的节点只有右子树时,直接将当前节点指向其右子树
        else if(root->lchild==NULL&&root->rchild!=NULL)
        {
            *proot=root->rchild;
            //销毁要删除的节点
            DestroySearchNode(root);
        }
        //(4)删除的节点有左右子树时
        else
        {
            //先找到其右子树中最小节点,由于二叉搜索树的特点,故最小节点一定在其右子树的最左边
            SearchNode* min_node=root->rchild;
            while(min_node->lchild!=NULL)
            {
                min_node=min_node->lchild;
            }
            //将要删除的节点的值置为最小节点的值                                                                         
            root->data=min_node->data;
            //从当前节点的右子树出发删除最小节点
            SearchTreeRemove(&root->rchild,min_node->data);
        }
    }
}

以上就是递归实现的关于二叉搜索树的相关操作!

猜你喜欢

转载自blog.csdn.net/tongxuexie/article/details/80347339