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

在我的博客:二叉搜索树的相关操作(递归实现)中对二叉搜索树进行了初始化、销毁以及插入、查找、删除操作!下面我将在其基础上对插入、查找、删除操作使用非递归的方式实现。

1. 按值插入

(1)空树时,直接插入在根节点

(2)非空树时:① 先找到合适的可以插入的空位置(循环找到空位置)

                        ② 创建新节点

                        ③ 判断将其插入在其父节点的左子树还是右子树

//思路:1.空树时,直接插入在根节点
//      2.非空树时:(1)先找到合适的可以插入的空位置
//                  (2)创建新节点
//                  (3)判断将其插入在其父节点的左子树还是右子树
void SearchTreeInsertEx(SearchNode** proot,SearchNodeType value)
{
    //非法输入
    if(proot==NULL)
    {
        return;
    }
    //1.空树时,直接插入在根节点
    if(*proot==NULL)                                                                                                     
    {
        SearchNode* new_node=CreateSearchNode(value);
        *proot=new_node;
    }
    //2.非空树时
    //当前节点
    SearchNode* cur=*proot;
    //当前节点的父节点
    SearchNode* pre=NULL;
    while(1)
    {
        //判断是否找到合适的位置进行插入
        if(cur==NULL)
        {
            break;
        }
        //(1)先找到合适的可以插入的空位置
        if(value<cur->data)
        {                                                                                                                
            //当value<cur->data时,继续在其左子树中找
            pre=cur;
            cur=cur->lchild;
        }
        else if(value>cur->data)
        {
            //当value>cur->data时,继续在其右子树中找
            pre=cur;
            cur=cur->rchild;
        }
        else
        {
            //当相等时,约定插入失败
            return;
        }
    }
    //(2)创建新节点
    SearchNode* new_node=CreateSearchNode(value);
    //(3)判断将其插入在其父节点的左子树还是右子树                                                                        
    if(value<pre->data)
    {
        //插入在其父节点的左子树中
        pre->lchild=new_node;
    }
    else if(value>pre->data)
    {
        //插入在其父节点的右子树中
        pre->rchild=new_node;
    }
}

2. 按值查找

遍历二叉搜索树去查找:

(1)空树时直接return NULL

(2)非空树时,利用while循环去比较查找值与节点值的大小,此时又分为三种情况:

①当查找值<节点值时,在其节点的左子树中继续查找

②当查找值>节点值时,在其节点的右子树中继续查找

③当查找值=节点值时,说明找到了。

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

//思路:在二叉搜索树中遍历查找
//1.空树时直接return NULL
//2.非空树时,利用while循环去比较查找值与节点值的大小
SearchNode* SearchTreeFindEx(SearchNode* root,SearchNodeType find)
{
    //1.空树时直接return NULL
    if(root==NULL)
    {
        return NULL;
    }
    //2.非空树时
    SearchNode* cur=root;
    while(cur!=NULL)
    {
        //循环判断是否找到find节点
        if(find<cur->data)
        {
            //在左子树中继续查找
            cur=cur->lchild;
        }
        else if(find>cur->data)
        {
            //在右子树中继续查找
            cur=cur->rchild;
        }
        else
        {                                                                                                                
            //找到了,跳出循环
            break;
        }
    }
    return cur;
    //此时返回的cur有可能是找到的节点的指向也有可能是未找到的空指针NULL
}

3. 按值删除

(1)找到要删除的节点

(2)分情况讨论要删除的节点:

①当要删除的节点无子树时,将其父节点对应的子树指向NULL

②当要删除的节点只有左子树时,将其父节点对应的子树指向要删除的节点的左子树

③当要删除的节点只有右子树时,将其父节点对应的子树指向要删除的节点的右子树

④当要删除的节点有左右子树时,找到要删除的节点的右子树的最小节点并用其data覆盖要删除的节点的data最后删除最小节点

//思路:1.找到要删除的节点
//      2.分情况讨论要删除的节点:
//      (1)当要删除的节点无子树时,将其父节点对应的子树指向NULL
//      (2)当要删除的节点只有左子树时,将其父节点对应的子树指向要删除的节点的左子树
//      (3)当要删除的节点只有右子树时,将其父节点对应的子树指向要删除的节点的右子树
//      (4)当要删除的节点有左右子树时,找到要删除的节点的右子树的最小节点并用其data覆盖要删除的节点的data最后删除最小节点
void SearchTreeRemoveEx(SearchNode** proot,SearchNodeType to_remove)
{
    //非法输入
    if(proot==NULL)
        return;
    //空树时
    if(*proot==NULL)
        return;                                                                                                          
    //1.找到要删除的节点
    SearchNode* remove=*proot;
    //parent表示要删除节点的父节点
    SearchNode* parent=NULL;
    while(1)
    {
        //没找到要删除的节点
        if(remove==NULL)
            return;
        //当to_remove<remove->data时,在remove的左子树中继续找
        if(to_remove<remove->data)
        {
            parent=remove;
            remove=remove->lchild;
        }
        //当to_remove>remove->data时,在remove的右子树中继续找
        else if(to_remove>remove->data)
        {
            parent=remove;                                                                                               
            remove=remove->rchild;
        }
        //当相等时表示找到了
        else
        {
            break;
        }
    }
    //2.判断要删除的节点的情况
    //(1)要删除的节点没有子树时
    if(remove->lchild==NULL&&remove->rchild==NULL)
    {
        //①要删除的节点为根节点时
        if(remove==*proot)
        {
            *proot=NULL;
        }
        //②要删除的节点不是根节点时
        else                                                                                                             
        {
            //判断要删除的节点为其父节点的左子树还是右子树
            if(remove->data<parent->data)
            {
                //为左子树
                parent->lchild=NULL;
            }
            else
            {
                //为右子树
                parent->rchild=NULL;
            }
        }
        DestroySearchNode(remove);
    }
    //(2)要删除的节点只有左子树时
    else if(remove->lchild!=NULL&&remove->rchild==NULL)
    {
        //①要删除的节点为根节点时                                                                                       
        if(remove==*proot)
        {
            *proot=remove->lchild;
        }
        //②要删除的节点不是根节点时
        else
        {
            //判断要删除的节点为其父节点的左子树还是右子树
            if(remove->data<parent->data)
            {
                //为左子树
                parent->lchild=remove->lchild;
            }
            else
            {
                //为右子树
                parent->rchild=remove->lchild;
            }
        }                                                                                                                
        DestroySearchNode(remove);
    }
    //(3)当要删除的节点只有右子树时
    else if(remove->lchild==NULL&&remove->rchild!=NULL)
    {
        //①要删除的节点为根节点时
        if(remove==*proot)
        {
            *proot=remove->rchild;
        }
        //②要删除的节点不是根节点时
        else
        {
            //判断要删除的节点为其父节点的左子树还是右子树
            if(remove->data<parent->data)
            {
                //为左子树
                parent->lchild=remove->rchild;
            }                                                                                                            
            else
            {
                //为右子树
                parent->rchild=remove->rchild;
            }
        }
        DestroySearchNode(remove);
    }
    //(4)要删除的节点有左右子树时
    else
    {
        //①先找到其右子树中最小节点
        SearchNode* min=remove->rchild;
        SearchNode* min_parent=remove;
        //注意这是二叉搜索树故最小的节点一定在最左边
        while(min->lchild!=NULL)
        {
            min_parent=min;
            min=min->lchild;                                                                                             
        }
        //找到min后,说明min是其右子树中最左边的树,故min没有左子树
        //②用最小节点min的值覆盖要删除的节点的值
        remove->data=min->data;
        //③此时就是删除min节点
        if(min->data<min_parent->data)
        {
            //注意:找到的min的左子树为空树,故直接将min的右子树托付给其父节点
            min_parent->lchild=min->rchild;
        }
        else
        {
            min_parent->rchild=min->rchild;
        }
    DestroySearchNode(min);
    }
}       

4. 测试代码

以下代码是测试以上操作正确与否,以供参考!

/*=======测试代码=======*/
//测试SearchTreeTnsert
//为了验证测试结果的正确与否,编写先序遍历和中序遍历函数
void PreOrder(SearchNode* root)
{
    //空树
    if(root==NULL)
    {
        return;
    }
    printf("%c ",root->data);
    PreOrder(root->lchild);
    PreOrder(root->rchild);                                                                                              
}
void InOrder(SearchNode* root)
{
    //空树
    if(root==NULL)
    {
        return;
    }
    InOrder(root->lchild);
    printf("%c ",root->data);
    InOrder(root->rchild);
}
void Test_SearchTreeInsert()
{
    HEADER;
    SearchNode* root;
    SearchTreeInit(&root);
    SearchTreeInsertEx(&root,'a');
    SearchTreeInsertEx(&root,'b');                                                                                       
    SearchTreeInsertEx(&root,'f');
    SearchTreeInsertEx(&root,'d');
    SearchTreeInsertEx(&root,'e');
    SearchTreeInsertEx(&root,'c');
    printf("先序遍历结果:\n");
    PreOrder(root);
    printf("\n");
    printf("中序遍历结果:\n");
    InOrder(root);
    printf("\n");
}
//测试SearchNodeFind
void Test_SearchTreeFind()
{
    HEADER;
    SearchNode* root;
    SearchTreeInit(&root);
    SearchTreeInsertEx(&root,'a');
    SearchTreeInsertEx(&root,'b');                                                                                       
    SearchTreeInsertEx(&root,'f');
    SearchTreeInsertEx(&root,'d');
    SearchTreeInsertEx(&root,'e');
    SearchTreeInsertEx(&root,'c');
    SearchNode* ret=NULL;
    ret=SearchTreeFindEx(root,'a');
    printf("expected a,actual %c\n",ret->data);
    ret=SearchTreeFindEx(root,'d');
    printf("expected d,actual %c\n",ret->data);
    ret=SearchTreeFindEx(root,'h');
    printf("expected NULL,actual %p\n",ret);
}
//测试SearchTreeRemove
void Test_SearchTreeRemove()
{
    HEADER;
    SearchNode* root;
    SearchTreeInit(&root);
    SearchTreeInsertEx(&root,'a');                                                                                       
    SearchTreeInsertEx(&root,'b');
    SearchTreeInsertEx(&root,'f');
    SearchTreeInsertEx(&root,'d');
    SearchTreeInsertEx(&root,'e');
    SearchTreeInsertEx(&root,'c');
    SearchTreeRemoveEx(&root,'c');
    printf("先序遍历结果:\n");
    PreOrder(root);
    printf("\n");
    printf("中序遍历结果:\n");
    InOrder(root);
    printf("\n");
}
/*========主函数======*/
int main()
{
    Test_SearchTreeInsert();
    Test_SearchTreeFind();
    Test_SearchTreeRemove();                                                                                             
    return 0;
}
以上就是非递归实现的二叉搜索树的相关操作!

猜你喜欢

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