二叉查找树

bsTree.h

#ifndef BSTREE_H_INCLUDED
#define BSTREE_H_INCLUDED

#undef NULL
#ifdef __cplusplus
    #define NULL 0
#else
    #define NULL ((void *)0)
#endif

/*
二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)
它是一棵空树或者具有以下性质:
    若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值;
    若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值;
    它的左、右子树也分别为二叉排序树。
后继:
    如果二叉查找树的关键字互不相同,则一个节点x的后继是大于x.key的最小关键字节点。
简单来说:前驱就是节点x左子树key最大的节点,后继是节点x右子树中key最小的节点。
*/

typedef struct tag_bsTree
{
    struct tag_bsTree *pLNode;  //左孩子
    struct tag_bsTree *pRNode;  //右孩子
    struct tag_bsTree *pPNode;  //双亲、
    int key;
}bsTree;

//为树分配一个新节点
bsTree* getBsNode(int key);

/*
将一个节点插入bsTree。按照插入节点的大小以及二叉树的性质插入
*/
bsTree* bsInsert(bsTree *pNewTreeNode, bsTree* pstBsRoot);

//查找
bsTree* bsSearch(int key , bsTree* pstBsRoot);

/*
在二叉查找树种删除一个元素,返回bool。
分三种情况进行处理:
  1.p为叶子节点,直接删除该节点,再修改其父节点的指针
  2.p为单支节点(即只有左子树或右子树)。让p的子树与p的父亲节点相连,删除p即可
  3.p的左子树和右子树均不空。找到p的后继y,因为y一定没有左子树,所以可以删除y,并让y的父亲节点成为y的右子树的父亲节点,并用y的值代替p的值;或者方法二是找到p的前驱x,x一定没有右子树,所以可以删除x,并让x的父亲节点成为y的左子树的父亲节点。如图c。
*/
bsTree* bsDel(bsTree *pstDelNode, bsTree *pstBsRoot);

//获取pstBsRoot之后的最大key所在位置
bsTree *bsGetMaxNode(bsTree *pstBsRoot);
//获取pstBsRoot之后的最小key所在位置
bsTree *bsGetMinNode(bsTree *pstBsRoot);
//获取pNode的后继
bsTree* bsGetNext(bsTree *pNode);
//获取pNode的前驱
bsTree* bsGetPre(bsTree *pNode);

#endif // BSTREE_H_INCLUDED


bsTree.c

#include "bsTree.h"
#include "memory.h"

#define RETURN_ERR -1
#define RETURN_OK 0

bsTree* getBsNode(int key)
{
    bsTree *pNewNode = NULL;

    pNewNode = (bsTree*)malloc(sizeof(bsTree));
    if (NULL == pNewNode)
    {
        return NULL;
    }

    memset(pNewNode, 0, sizeof(bsTree));
    pNewNode->key = key;

    return pNewNode;
}

/*
函数功能:插入一个节点到二叉查找树
函数入参:bsTree *pNewTreeNode 待插入节点
          bsTree* pstBsRoot 二叉树的根节点
函数出参:无
函数返回值:二叉树根节点(有可能改变二叉树根节点的位置)
其他: 该函数也可以使用递归实现
*/
bsTree* bsInsert(bsTree *pNewTreeNode, bsTree* pstBsRoot)
{
    bsTree *pstTmp = NULL;
    bsTree *pstPar = NULL;  //父节点的位置

    //如果新节点是空,则入参错误
    if (NULL == pNewTreeNode)
    {
        return NULL;   //返回NULL,要求调用者需要先判断返回值,返回值不空才能修改根节点的位置
    }

    pstTmp = pstBsRoot;

    while (NULL != pstTmp)
    {
        pstPar = pstTmp;    //先保存父节点

        if (pNewTreeNode->key <= pstTmp->key)      //严格来说如果等于条件成立,说明节点存在不允许再次插入,需要清理pNewTreeNode的内存
        {
            pstTmp = pstTmp->pLNode;
        }
        else
        {
            pstTmp = pstTmp->pRNode;
        }
    }

    pNewTreeNode->pPNode = pstPar;

    if (NULL == pstPar)
    {
        pstBsRoot = pNewTreeNode;
    }
    else if (pNewTreeNode->key <= pstPar->key)
    {
        pstPar->pLNode = pNewTreeNode;
    }
    else
    {
        pstPar->pRNode = pNewTreeNode;
    }

    return pstBsRoot;
}

/*
函数功能:根据key确定节点在二叉查找树的位置
函数入参:int key 待查找节点的key值
          bsTree* pstBsRoot 树的根节点
函数出参:无
函数返回值:key对应的节点在二叉查找树中的位置,找不到返回NULL
其他: 该函数也可以使用递归实现
*/
bsTree* bsSearch(int key , bsTree* pstBsRoot)
{
    bsTree *pTmp = pstBsRoot;

    while (NULL != pTmp)
    {
        if (key < pTmp->key)
        {
            pTmp = pTmp->pLNode;
        }
        else if (key > pTmp->key)
        {
            pTmp = pTmp->pRNode;
        }
        else
        {
            break;
        }
    }

    return pTmp;
}

/*
函数功能:删除一个节点
函数入参:bsTree *pstDelNode  待删除节点的位置
          bsTree* pstBsRoot 二叉树的根节点
函数出参:无
函数返回值:删除后的根节点
其他:
*/
bsTree* bsDel(bsTree *pstDelNode, bsTree *pstBsRoot)
{
    bsTree *pTmp1 = NULL;
    bsTree *pTmp2 = NULL;

    //待删除节点的左孩子或者右孩子为空
    if ((NULL == pstDelNode->pLNode) || (NULL == pstDelNode->pRNode))
    {
        pTmp2 = pstDelNode;
    }
    else
    {
        pTmp2 = bsGetNext(pstDelNode);  //pstDelNode的左右孩子都不为空的时候,需要找到该节点的后继
    }

    if (NULL != pTmp2->pLNode)
    {
        pTmp1 = pTmp2->pLNode;      //后继节点的左孩子不为空,则保持后继的左孩子
    }
    else
    {
        pTmp1 = pTmp2->pRNode;      //此时pTmp1,有可能为空
    }

    if (NULL != pTmp1)
    {
        pTmp1->pPNode = pTmp2->pPNode;
    }

    if (NULL == pTmp2->pPNode)
    {
        pstBsRoot = pTmp1;
    }
    else if (pTmp2 == pTmp2->pPNode->pLNode)
    {
        pTmp2->pPNode->pLNode = pTmp1;
    }
    else
    {
        pTmp2->pPNode->pRNode = pTmp1;
    }

    if (pTmp2 != pstDelNode)
    {
        pstDelNode->key = pTmp2->key;
    }

    if (NULL != pTmp2)
    {
        free(pTmp2);
    }

    return pstBsRoot;   //删除节点的时候根节点肯定存在(或为NULL)
}

//获取pstBsRoot之后的最大key所在位置
bsTree *bsGetMaxNode(bsTree *pstBsRoot)
{
    bsTree *pTmp = pstBsRoot;

    if (NULL == pstBsRoot)
    {
        return NULL;    //子树不存在
    }

    while (NULL != pTmp->pRNode)    //为空的时候就查找到了哦
    {
        pTmp = pTmp->pRNode;
    }

    return pTmp;
}

//获取pstBsRoot之后的最小key所在位置
bsTree *bsGetMinNode(bsTree *pstBsRoot)
{
    bsTree *pTmp = pstBsRoot;

    if (NULL == pstBsRoot)
    {
        return NULL;    //子树不存在
    }

    while (NULL != pTmp->pLNode)
    {
        pTmp = pTmp->pLNode;
    }

    return pTmp;
}

bsTree* bsGetNext(bsTree *pNode)
{
    bsTree *pPer = NULL;    //父节点
    bsTree *pTmp = NULL;

    if (NULL != pNode->pRNode)
    {
        return bsGetMinNode(pNode->pRNode);     //右孩子存在,直接查找子树的最小key
    }

    /*
        如果pNode没有右孩子。则pNode有以下两种可能:
        (01) pNode是"一个左孩子",则"pNode的后继结点"为 "它的父结点"。
        (02) pNode是"一个右孩子",则查找"pNode的最低的父结点,并且该父结点要具有左孩子"
    */
    pTmp = pNode;
    pPer = pNode->pPNode;
    while ((NULL != pPer) && (pTmp == pPer->pRNode))   //父节点不为空且pNode是有孩子,查找pNode的父节点的具有左孩子的先祖
    {
        pTmp = pPer;
        pPer = pPer->pPNode;
    }

    return pPer;
}

bsTree* bsGetPre(bsTree *pNode)
{
    bsTree *pPer = NULL;    //父节点
    bsTree *pTmp = NULL;

    if (NULL != pNode->pLNode)
    {
        return bsGetMaxNode(pNode->pRNode);     //左孩子存在,直接查找子树的最大key
    }

    /*
        如果pNode没有左孩子。则pNode有以下两种可能:
        (01) pNode是"一个右孩子",则"pNode的后继结点"为 "它的父结点"。
        (02) pNode是"一个左孩子",则查找"pNode的父结点,并且该父结点要具有右孩子的父节点(不好用语言表述)"
    */
    pTmp = pNode;
    pPer = pNode->pPNode;
    while ((NULL != pPer) && (pTmp == pPer->pLNode))
    {
        pTmp = pPer;
        pPer = pPer->pPNode;
    }

    return pPer;
}

main.c

#include <stdio.h>
#include "bsTree.h"

bsTree *g_pstBsRoot = NULL;

//将i,转换成key,用于构造比较平衡的二叉树
int getKey(int i)
{
    switch (i)
    {
        case 0:
            return 3;
        case 1:
            return 2;
        case 2:
            return 4;
        case 3:
            return 8;
        case 4:
            return 7;
        case 5:
            return 11;
        case 6:
            return 9;
        case 7:
            return 12;
        case 8:
            return 10;
        case 9:
            return 11;
        default :
            return 1;
    }
}
void printBsTree(bsTree *pTreeRoot)
{
    if (NULL == pTreeRoot)
    {
        return;
    }

    printBsTree(pTreeRoot->pLNode);
    //访问根节点
    printf("%d\t", pTreeRoot->key);
    printBsTree(pTreeRoot->pRNode);

    return;
}

int main()
{
    bsTree *pTmp = NULL;
    bsTree *pNew = NULL;
    int i;
    int j;

    pNew = getBsNode(5);
    if (NULL == pNew)
    {
        exit(0);
    }

    pTmp = bsInsert(pNew, g_pstBsRoot);
    if (0 == pTmp)
    {
         exit(0);
    }
    g_pstBsRoot = pTmp;     //判断之后再赋值,防止插入失败导致根节点指向空,从而造成内存泄漏

    for (i = 0; i < 11; i++)
    {
        j = getKey(i);
        pNew = getBsNode(j);
        if (NULL == pNew)
        {
            exit(0);
        }

        pTmp = bsInsert(pNew, g_pstBsRoot);
        if (NULL == pTmp)
        {
             exit(0);
        }
        g_pstBsRoot = pTmp;
    }

    printBsTree(g_pstBsRoot);       //执行中序遍历之后,key按照从小到大的顺序排列

    pTmp = bsSearch(0, g_pstBsRoot);
    printf("\npTmp = %p", pTmp);      //打印key对应节点的位置

    pTmp = bsSearch(4, g_pstBsRoot);
    pTmp = bsGetPre(pTmp);     //获取4的前驱
    printf("\n%d", pTmp->key);
    pTmp = bsSearch(4, g_pstBsRoot);
    pTmp = bsGetNext(pTmp);     //获取4的后继
    printf("\n%d", pTmp->key);

    pTmp = bsSearch(1, g_pstBsRoot);
    pTmp = bsGetPre(pTmp);  //获取1的前驱,应该返回NULL
    printf("\npTmp = %p\n", pTmp);

    pTmp = bsSearch(5, g_pstBsRoot);
    g_pstBsRoot = bsDel(pTmp, g_pstBsRoot);
    printBsTree(g_pstBsRoot);       //执行中序遍历之后,key按照从小到大的顺序排列

    return 0;
}




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值