二叉查找树查找,插入,删除算法详解(C语言实现,注释最全,单步调试通过)

二叉查找树查找,插入,删除算法详解(C语言实现,注释最全,单步调试通过)

二叉查找树的定义

二叉排序树 (Binary Sort Tree) 又称二叉查找树,它是一种对排序和查找都很有用的特殊二叉树。

二叉排序树或者是一棵空树,或者是具有下列性质的二叉树:
(1) 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2) 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3) 它的左、 右子树也分别为二叉排序树。

二叉排序树是递归定义的。 由定义可以得出二叉排序树的一个重要性质:中序遍历一棵二叉
树时可以得到一个结点值递增的有序序列。

二叉查找树的实现

#include <stdio.h>
#include <stdlib.h>
#define true 1
#define false 0
#define ElemType int
#define KeyType int

/**
 * 二叉排序树的结点结构定义
 */ 
typedef struct _BinaryNode
{
    /* data */
    ElemType data;
    struct _BinaryNode *lchild;
    struct _BinaryNode *rchild;
}BinaryNode,*BinaryTree;

/**
 * @brief 二叉查找树查找算法
 *
 * @param[in] Node 当前查找的二叉查找树的结点
 * @param[in] KeyData 待查找的数据
 * @param[in] FatherNode 当前查找的二叉查找树的结点的父结点
 * @param[out] FoundNode 如果找到,存放找到的结点;如果没找到,值为空。
 * @param[out] FatherOfFoundNode 如果找到,存放找到的结点的父结点;如果没找到,存放最后查找的那个结点。
 * 
 * @return int
 * @retval 1 查找成功
 * @retval 0 查找失败
 */
int SearchBST(BinaryTree Node,KeyType KeyData,BinaryTree FatherNode,BinaryTree *FoundNode,BinaryTree *FatherOfFoundNode)
{
    /**
     * 此函数是递归的,如果Node结点为空,说明查无可查,查找失败。
     * 给函数的输出参数FatherOfFoundNode指向最后一个查找的结点。
     */ 
    if(!Node)
    {
        *FatherOfFoundNode = FatherNode;
        *FoundNode = NULL;

        return false;
    }
    /**
     * 此函数是递归的,如果Node结点的数据与查找的数据相同,说明已查到。
     * 给函数的输出参数FoundNode指向查找到的结点,函数的输出参数FatherOfFoundNode指向查找结点的父结点。
     */ 
    if(KeyData == Node->data)
    {
        *FoundNode = Node;
        *FatherOfFoundNode = FatherNode;
        return true;
    }
    /**
     * 此函数是递归的,如果Node结点的数据比查找的数据大,说明待查找数据可能在Node结点的左子树中。查找左子树。
     */ 
    else if(KeyData < Node->data)
    {
        return SearchBST(Node->lchild, KeyData, Node, FoundNode,FatherOfFoundNode);
    }
    /**
     * 此函数是递归的,如果Node结点的数据比查找的数据小,说明待查找数据可能在Node结点的右子树中。查找右子树。
     */ 
    else
    {
        return SearchBST(Node->rchild, KeyData, Node, FoundNode,FatherOfFoundNode);
    }

}
/**
 * @brief 二叉查找树查找最小值算法
 *
 * @param[in] BinaryNode 当前查找的二叉查找树的结点
 * 
 * @return BinaryTree 
 * @retval !NULL 查找到的结点指针
 * @retval NULL 查找失败
 */
BinaryTree FindBSTMin(BinaryTree BinaryNode)
{
    if(BinaryNode == NULL)
    {
        return NULL;
    }
    else
    {
        if(BinaryNode->lchild == NULL)
        {
            return BinaryNode;
        }
        else
        {
            return FindBSTMin(BinaryNode->lchild);
        }
    }

}
/**
 * @brief 二叉查找树查找最大值算法
 *
 * @param[in] BinaryNode 当前查找的二叉查找树的结点
 * 
 * @return BinaryTree 
 * @retval !NULL 查找到的结点指针
 * @retval NULL 查找失败
 */
BinaryTree FindBSTMax(BinaryTree BinaryNode)
{
    if(BinaryNode == NULL)
    {
        return NULL;
    }
    else
    {
        if(BinaryNode->rchild == NULL)
        {
            return BinaryNode;
        }
        else
        {
            return FindBSTMax(BinaryNode->rchild);
        }
    }

}

/**
 * @brief 二叉查找树查找算法
 *
 * @param[in] Tree 指向待查找的二叉查找树的结点
 * @param[in] Data 待查找的数据
 * 
 * @return int
 * @retval 1 插入成功
 * @retval 0 插入失败
 */
int InsertBST(BinaryTree *Tree,ElemType Data)
{
    BinaryTree FoundNode = NULL;
    BinaryTree FatherOfFoundNode = NULL;
    /**
     * 插入之前先查找,查找失败就做插入操作
     */ 
    if(!SearchBST(*Tree,Data,NULL,&FoundNode,&FatherOfFoundNode))
    {
        /**
         * 初始化插入结点
         */ 
        BinaryTree InsertNode = (BinaryTree)malloc(sizeof(BinaryNode));
        InsertNode->data = Data;
        InsertNode->lchild = NULL;
        InsertNode->rchild = NULL;
        /**
         * 最后一次查找的结点为空,说明整个二叉排序树是空树,此时插入的结点是根结点
         */ 
        if(!FatherOfFoundNode)
        {
            *Tree = InsertNode;
        }
        else if(Data < FatherOfFoundNode->data)
        {
            FatherOfFoundNode->lchild = InsertNode;
        }
        else
        {
            FatherOfFoundNode->rchild = InsertNode;
        }
        return true;
    }
    /**
     * 插入之前先查找,查找成功返回失败
     */ 
    else
    {
        return false;
    }
}
/**
 * @brief 二叉查找树删除算法
 *
 * @param[in] DeleteNode 指向待查找的二叉查找树的结点的指针
 * @param[in] FatherOfDeleteNode 指向待查找的二叉查找树结点的父结点的指针
 * 
 * @return int
 * @retval 1 插入成功
 * @retval 0 插入失败
 */
int DeleteNode(BinaryTree *DeleteNode,BinaryTree *FatherOfDeleteNode)
{
    BinaryTree FatherOfPrecursorForDeleteNode = NULL;//待删除结点的前驱结点的父亲
    BinaryTree PrecursorForDeleteNode = NULL;//待删除结点的前驱结点
    /**
     * 情况1:待删除结点DeleteNode本身就是叶子结点
     */ 
    if (!(*DeleteNode)->lchild && !(*DeleteNode)->rchild) {
        /**
         * 情况1.1:待删除结点DeleteNode本身就是叶子结点,且是为他父亲的左孩子,将父结点的左孩子结点指向空
         */ 
        if((*FatherOfDeleteNode)->lchild == *DeleteNode)
        {
            (*FatherOfDeleteNode)->lchild = NULL;
        }
        /**
         * 情况1.2:待删除结点DeleteNode本身就是叶子结点,且是为他父亲的右孩子,将父结点的右孩子结点指向空
         */ 
        else
        {
            (*FatherOfDeleteNode)->rchild = NULL;
        }
        /**
         * 释放待删除结点的内存
         */ 
        free(*DeleteNode);
        *DeleteNode = NULL;
    }
    /**
     * 情况2:右子树为空,左子树不为空,只需用待删除结点DeleteNode的父结点指向待删除结点DeleteNode的左子树即可;
     * 注意区分待删除结点是他父结点的左孩子还是右孩子
     */ 
    else if(!(*DeleteNode)->rchild)
    {
        if((*FatherOfDeleteNode)->lchild == *DeleteNode)
        {
            (*FatherOfDeleteNode)->lchild = (*DeleteNode)->lchild;
        }
        else
        {
            (*FatherOfDeleteNode)->rchild = (*DeleteNode)->lchild;
        }
        free(*DeleteNode);
        *DeleteNode = NULL;
        
    }
    /**
     * 情况3:左子树为空,右子树不为空,只需用待删除结点DeleteNode的父结点指向待删除结点DeleteNode的右子树即可;
     * 注意区分待删除结点是他父结点的左孩子还是右孩子
     */ 
    else if(!(*DeleteNode)->lchild)
    {
        if((*FatherOfDeleteNode)->rchild == *DeleteNode)
        {
            (*FatherOfDeleteNode)->rchild = (*DeleteNode)->rchild;
        }
        else
        {
            (*FatherOfDeleteNode)->lchild = (*DeleteNode)->rchild;
        }
        free(*DeleteNode);
        *DeleteNode = NULL;
        
    }
    else
    {
        /**
         * 情况4:左右子树均不为空,用待删除结点的前驱结点的data替换待删除结点的data,删除待删除结点的前驱结点
         */ 
        FatherOfPrecursorForDeleteNode = *DeleteNode;//待删除结点的前驱结点的父亲初始化为待删除结点,即默认待删除结点的前驱结点为其左子树
        PrecursorForDeleteNode = (*DeleteNode)->lchild;//待删除结点的前驱结点初始化,默认待删除结点的前驱结点为待删除结点的左子树
        //遍历,找到结点 DeleteNode 的直接前驱
        while (PrecursorForDeleteNode->rchild)
        {
            FatherOfPrecursorForDeleteNode = PrecursorForDeleteNode;
            PrecursorForDeleteNode = PrecursorForDeleteNode->rchild;
        }
        //循环之后PrecursorForDeleteNode即为待删除结点的前驱结点,FatherOfPrecursorForDeleteNode即为待删除结点的前驱结点的父亲
        
        //直接改变结点 DeleteNode 的值
        (*DeleteNode)->data = PrecursorForDeleteNode->data;
        //此种情况是待删除结点 DeleteNode 的左子树没有右子树,这时待删除结点的前驱结点为其左子树,待删除结点的前驱结点的父亲为待删除结点本身。
        if (FatherOfPrecursorForDeleteNode == *DeleteNode) {
            //直接将待删除结点的前驱结点的左子树上移即可
            (*DeleteNode)->lchild = PrecursorForDeleteNode->lchild;

        }
        else {
            //此时待删除结点的前驱结点不是他的左孩子,那么找到的待删除结点的前驱结点有如下特征:只存在有左孩子无右孩子或左右孩子均无的这两种情况。
            //改待删除结点的前驱结点的父亲FatherOfPrecursorForDeleteNode 指向待删除结点的前驱结点PrecursorForDeleteNode的左孩子结点
            //此种情况已经考虑待删除结点的前驱结点没有左孩子。
            FatherOfPrecursorForDeleteNode->rchild = PrecursorForDeleteNode->lchild;

        }
        //删除直接前驱结点
        free(PrecursorForDeleteNode);
        PrecursorForDeleteNode = NULL;
    }
    return true;
}

/**
 * @brief 二叉查找树删除整颗树算法
 *
 * @param[in] BinaryNode 待删除的二叉查找树的根结点
 * 
 * @return BinaryTree
 * @retval NULL 删除成功
 */
BinaryTree DeleteBST(BinaryTree BinaryNode)
{
    if(BinaryNode != NULL)
    {
        DeleteBST(BinaryNode->lchild);
        DeleteBST(BinaryNode->rchild);
        free(BinaryNode);
    }
    return NULL;
}

//测试案例
int main(void)
{
    int i;
    //int Squence[5] = {3, 4, 2, 5, 9};
    int Squence2[] = {8, 4, 10, 2, 6, 12, 5};
    BinaryTree Tree = NULL;
    for (i = 0; i < sizeof(Squence2)/sizeof(Squence2[0]);++i)
    {
        int ReturnCode = InsertBST(&Tree, Squence2[i]);
        printf("insert %d result %d\r\n", Squence2[i], ReturnCode);
    }
    BinaryTree FoundNode = NULL;
    BinaryTree FatherOfFoundNode = NULL;
    
    if(SearchBST(Tree,8,NULL,&FoundNode,&FatherOfFoundNode))
    {
        DeleteNode(&FoundNode,&FatherOfFoundNode);
    }

}
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值