寫了二叉排序樹,基本的插入,查找,刪除運算,下面是我的詳細代碼:
#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,然後再從二叉排序樹中刪去它的直接前驅(或直接後繼)。