二叉查找树

来源:互联网 发布:亿世界外包系统源码 编辑:程序博客网 时间:2024/06/05 09:19

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 0bsTree* 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;}




原创粉丝点击