写了二叉排序树,基本的插入,查找,删除运算,下面是我的详细代码:
#include <stdio.h>
#include <stdlib.h>
typedef struct BiTNode{//定义二叉排序树的结构
int data;//值
struct BiTNode *lchild,*rchild;//左右孩子
}BiTNode,*BiTree;
void inorder(BiTree T); //中序遍历
int DeleteBST(BiTree T,int key); //删除特定元素
int SearchBST(BiTree u,int key);//查找元素
BiTree InsertBST(BiTree T,int key);//插入元素
int main()//测试函数
{
int i,data,n,m;
BiTree tree = NULL;//定义BiTNode 型的指针
printf("Input your data:");
for(i = 0; i < 7; i++)//输入值
{
scanf("%d",&data);
tree = InsertBST(tree,data);//构造树
}
printf("中序遍历结果:");
inorder(tree);
printf("\n");
printf("输入要查找的元素:");
scanf("%d",&n);
printf("查找结果:");
printf("%d\n",SearchBST(tree,n));
printf("输入要删除的元素:");
scanf("%d",&m);
DeleteBST(tree,m);
inorder(tree);
system("pause");
return 0;
}
int SearchBST(BiTree u,int key)//二叉排序树、查找的递归算法
{
BiTree p = u;
if(!p)
return 0;
else if(key == p->data)
return 1;
else if(key > p->data)
return SearchBST(p->rchild,key);
else
return SearchBST(p->lchild,key);
}
BiTree InsertBST(BiTree T,int key)//二叉排序树的插入
{
BiTree f = T,p = T;
while(p)
{
if(p->data == key)//如果待插元素等于根结点的值 ,则不用插入
return T;//返回根结点
f = p;//用f记下查找路径上的最后一个访问的结点
p = (key < p->data)? p->lchild:p->rchild;//如果根结点值与待插元素不等,重置p,根据大小关系
}
p = (BiTNode *)malloc(sizeof(BiTNode));//申请待插元素所需空间
p->data = key;//待插元素赋给此结点
p->lchild = p->rchild = NULL;//将插入的元素所在结点的左右孩子均置为空
//插入操作:
if(T == NULL)//T为空,吧P赋给T
T = p;
else if(key < f->data)//否则,插入待插元素
f->lchild = p;//根据大小关系
else
f->rchild = p;//根据大小关系插入
return T;//返回树根
}
void inorder(BiTree T)//根据二叉排序树的特点,中序遍历后为顺序输出,元素已经排好序
{
if(T)
{
inorder(T->lchild);
printf("%d ",T->data);
inorder(T->rchild);
}
}
int DeleteBST(BiTree T,int key)//删除算法,删除其中一个关键字为key的结点
{
BiTree p = T,f,q,s,root = T;
while(p)
{
if(p->data == key)//找出关键字为key的结点
break;
f = p;//记录关键字key结点的父结点
p = (key < p->data)? p->lchild:p->rchild;//在P的左右子树中寻找
}
if(!p)
return 0;//二叉排序树中无关键字为key的结点
if(p->lchild == NULL && p->rchild == NULL)//P没有左右子树
{
if(p == T)
T = NULL;//删除的是根结点
else if(p == f->lchild)//f为关键字为key的结点p的父结点
f->lchild = NULL;//将f的左孩子置为空
else
f->rchild = NULL;//将f的右孩子置为空
free(p);//删除结点p
}
else if(p->lchild == NULL && p->rchild != NULL)
{//P无左子树,有右子树
if(f->lchild == p)
f->lchild = p->rchild;//将p的右子树链接到其父结点的左链上
else
f->rchild = p->rchild;//将p的右子树链接到其父结点的右链上
free(p);
}
else if(p->lchild != NULL && p->rchild == NULL)
{//p有左子树无右子树
if(f->lchild == p)
f->lchild = p->lchild;//将p的左子树链接到其父结点的左链上
else
f->rchild = p->lchild;//将p的左子树链接到其父结点的右链上
free(p);
}
else if(p->lchild != NULL && p->rchild != NULL)//p既有左子树又有右子树
{
q = p;
s = p->lchild;//转左
while(s->rchild)
{//然后向右到尽头
q = s;
s = s->rchild;//s指向被删节点的"前驱"(中序前驱)
}
p->data = s->data;//以p的中序前趋结点s代替p(即把s的数据复制到p中)
if(q != p)
q->rchild = s->lchild;//重接q的右子树
else
q->lchild = s->lchild;//重接q的左子树。
free(s);
}
}
其他操作我觉得都容易,在删除结点时,情况有点复杂,下面是一点小解析:
假设在二叉排序树上被删除结点时*p(指向节点的指针为p),其双亲结点为*f(结点指针为f),且不是一般性,可设*p是*f的左孩子。
分三种情况进行:
1. 若p结点为叶子节点,即p没有左右孩子,由于删除叶子节点不破坏整棵树的结构,则只需修改双亲节点的指针即可。
2. 若p结点只有左孩子或者只有右孩子,此时只要将其左孩子或右孩子直接成为其双亲结点f的左子树即可。
3. 若p结点左右子树均不空,先找到*p的中序前趋结点*s(注意*s是*p的左子树中的最右下的结点,它的右链域为空),然后有两种做法:
其一是令p的左子树为f的左子树,而p的右子树为s的右子树;其二是令p的直接前驱(或直接后继)代替p,然后再从二叉排序树中删去它的直接前驱(或直接后继)。