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
#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;
}