二叉排序樹 分析、梳理、程式碼總結
二叉排序樹(binary sort tree)
二叉排序樹的定義
二叉排序樹或是一棵空樹,或是具有如下性質的非空二叉樹:
(1)若它的左子樹非空,則其左子樹所有結點的關鍵字值均小於其根結點的關鍵字值
(2)若它的右子樹非空,則其右子樹所有結點的關鍵字值均大於其根結點的關鍵字值
(3)它的左右子樹也分別為一棵二叉排序樹
二叉排序樹的重要特徵:對二叉排序樹進行中序遍歷可以得到一個關鍵字有序的序列
二叉排序樹的儲存:二叉連結串列
/*二叉排序樹的二叉連結串列儲存結構*/ typedef struct BTNode{ Elemtype key; /*記錄的關鍵字,忽略記錄的其他資料項*/ struct BTNode *lchild,*rchild; }BTNode,*BSTree;
二叉排序樹的查詢:
二叉排序樹不空的時候,將給定值key和結點的關鍵字比較,相等查詢成功,小於查詢左子樹,大於查詢右子樹,返回空查詢失敗。
查詢過程實際上是從根結點到結點的路徑,即該層的深度。
/*二叉排序樹的查詢*/ BTNode *SearchBST(BSTree T,int key) /*T指向二叉排序樹的根結點*/ { if(T==NULL) /*空樹,查詢失敗*/ return NULL; if(key==T->key) /*查詢成功*/ return T; if(key<T->key) return SearchBST(T->lchild,key); /*查詢左子樹*/ if(key>T->key) return SearchBST(T->rchild,key); /*查詢右子樹*/ }
二叉排序樹的插入
插入結點的條件:查詢不成功
基本思想:(1)若二叉排序樹為空,則新結點作為二叉排序樹的根結點
(2)若給定結點的關鍵字的值小於根結點的值,插入到左子樹中
(3)若給定結點的關鍵字的值大於根結點的值,插入到右子樹中
/*二叉排序樹的插入*/ BSTree InsertBST_key(BSTree T,int key) {/*在以T為根結點的二叉排序樹上查詢關鍵字為key的記錄,若不存在將其插入*/ BTNode *s=SearchBST(T,key); if(s!=NULL) { printf("\n關鍵字%d已存在!\n",s->key); return T; } s=(BTNode *)malloc(sizeof(BTNode)); /*生成一個結點空間*/ if(s==NULL) printf("Error\n"); s->key=key; s->lchild=s->rchild=NULL; T=InsertBST(T,s); /*在二叉排序樹上插入該結點*/ return T; }
/*在二叉排序樹上插入一個結點*/
BSTree InsertBST(BSTree T,BTNode *s)
{ /*在以T為根的二叉樹上插入一個指標s所指向的結點並返回根指標T*/
if(T==NULL) /*如果T是空樹,則新插入的結點S作為新的樹根*/
T=s;
else
{
if(s->key<T->key)
T->lchild=InsertBST(T->lchild,s); /*遞迴插到T的左子樹中*/
if(s->key>T->key)
T->lchild=InsertBST(T->lchild,s); /*遞迴插到T的右子樹中*/
}
return T;
}
二叉排序樹的建立
二叉樹的建立過程實際上就是一個查詢插入的過程,每次插入的新結點都是二叉樹上新的葉子結點,操作時不必像靜態查詢移動其他結點,只需改動新葉子結點的父結點的左或右指標,由空變為非空即可。二叉排序樹既擁有類似折半查詢的特性,又採用連結串列作為儲存結構。
/*二叉排序樹的建立*/
BSTree CreateBST() /*由空樹開始,輸入關鍵字序列,建立一棵二叉排序樹*/
{
BSTree T=NULL,s=NULL;
int key;
printf("輸入關鍵字序列,輸入-1結束\n");
while(1)
{
scanf("%d",&key);
if(key==-1)
break;
s=(BTNode *)malloc(sizeof(BTNode)); /*生成一個結點空間*/
s->key=key;
s->lchild=s->rchild=NULL;
T=InsertBST(T,s); /*在二叉排序樹中插入該結點*/
}
return T;
}
二叉排序樹的刪除
假設待刪除的結點為*p,f指向結點*p的雙親,s指向*p的左子樹中關鍵字值最大的結點,q指向s的雙親
刪除分為3中情況:
(1)*p為葉子結點,即其左右結點為空-----將其雙親結點f的lchild或rchild置空,刪除結點
(2)*p只有一棵非空子樹(左子樹或右子樹)
只有左子樹-----用左子樹的根結點取代要刪除的結點
只有右子樹-----用右子樹的根結點取代要刪除的結點
(3)*p左右子樹均非空-----用*p左子樹中關鍵字最大的結點*s替代*p,若*s有左孩子,將左孩子作為*s的雙親結點*q的右孩子
為了刪除結點後重接子樹,要修改查詢演算法
/*為了刪除操作而修改的二叉排序樹查詢*/
BTNode *SearchBST_F(BSTree T,int key,BSTree *F)
{ /*T指向根結點,*F儲存指向key的雙親的指標*/
/*查詢成功,返回指向key的記錄指標,查詢失敗返回NULL*/
if(T==NULL)
return NULL;
if(key==T->key)
return T;
*F=T;
if(key<T->key)
return SearchBST_F(T->lchild,key,F);
if(key>T->key)
return SearchBST_F(T->rchild,key,F);
}
/*在二叉排序樹中查詢並刪除關鍵字為key的記錄*/
BSTree SearchDeleteBST(BSTree T,int key)
{
BTNode *f=NULL,*pNULL;
p=SearchBST_F(T,key,&f); /*查詢key,p指向key,f指向雙親*/
if(p!=NULL)
T=DeleteBST(T,p,f);
else
printf("關鍵字為key的記錄不存在!\n");
return T;
}
二叉排序樹的刪除演算法
/*二叉排序樹的刪除*/
BSTree DeleteBST(BSTree T,BTNode *p,BTNode *f)
{/*刪除p指標指向的結點,f指向*p的雙親結點*/
/*T是指向根結點的指標*/
BTNode *par,*s;
int kind;
if(p->lchild==NULL&&p->rchild==NULL) /*情況1,*p為葉子結點*/
kind=1;
else if(p->rchild==NULL) /*情況2,*p只有左子樹*/
kind=2;
else if(p->lchild==NULL) /*情況3,*p只有右子樹*/
kind=3;
else /*情況4,*p左右子樹非空*/
kind=4;
switch(kind)
{
case 1: if(f==NULL) /*p指向根結點,樹中只有根結點*/
T=NULL; /*刪除結點*p,T變空樹*/
else
{
if(f->lchild==p) /* *p是*f的左孩子*/
f->lchild=NULL;
else /* *p是*f的右孩子*/
f->rchild==NULL;
}
free(p); /*刪除結點釋放空間*/
break;
case 2: if(f==NULL) /*f為NULL,*p為根結點,且只有左子樹*/
T=p->lchild;
else
{
if(f->lchild==p) /* *p是*f的左子樹*/
f->lchild=p->lchild;
else /* *p是*f的右子樹*/
f->rchild=p->lchild;
}
free(p); /*刪除結點釋放空間*/
break;
case 3: if(f==NULL) /*f為NULL,*p為根結點,且只有左子樹*/
T=p->rchild;
else
{
if(p==f->lchild)
f->lchild=p->rchild;
else
f->rchild=p->rchild;
}
free(p);
break;
case 4: par=p;
s=p->lchild;
while(s->rchild!=NULL) /*找到p左子樹的關鍵字最大(最右下)的結點*/
{
par=s;
s=s->rchild;
}
p->key=s->key; /*s結點的值覆蓋p結點的值*/
if(par==p) /*處理特殊情況,*p的左孩子為*s*/
par->lchild=s->lchild;
else
par->rchild=s->lchild; /*s的左子樹連線到s的雙親結點作為雙親結點的右子樹*/
free(s); /*注意不是free(p),因為p的關鍵字被s的關鍵字覆蓋,s已無用*/
}
return T;
}
完整程式碼如下:
#include <iostream>
#include<stdio.h>
#include<malloc.h>
using namespace std;
typedef int Elemtype;
/*二叉排序樹的二叉連結串列儲存結構*/
typedef struct BTNode{
Elemtype key; /*記錄的關鍵字,忽略記錄的其他資料項*/
struct BTNode *lchild,*rchild;
}BTNode,*BSTree;
/*二叉排序樹的查詢*/
BTNode *SearchBST(BSTree T,int key) /*T指向二叉排序樹的根結點*/
{
if(T==NULL) /*空樹,查詢失敗*/
return NULL;
if(key==T->key) /*查詢成功*/
return T;
if(key<T->key)
return SearchBST(T->lchild,key); /*查詢左子樹*/
if(key>T->key)
return SearchBST(T->rchild,key); /*查詢右子樹*/
}
/*在二叉排序樹上插入一個結點*/
BSTree InsertBST(BSTree T,BTNode *s)
{ /*在以T為根的二叉樹上插入一個指標s所指向的結點並返回根指標T*/
if(T==NULL) /*如果T是空樹,則新插入的結點S作為新的樹根*/
T=s;
else
{
if(s->key<T->key)
T->lchild=InsertBST(T->lchild,s); /*遞迴插到T的左子樹中*/
if(s->key>T->key)
T->lchild=InsertBST(T->lchild,s); /*遞迴插到T的右子樹中*/
}
return T;
}
/*二叉排序樹的插入*/
BSTree InsertBST_key(BSTree T,int key)
{/*在以T為根結點的二叉排序樹上查詢關鍵字為key的記錄,若不存在將其插入*/
BTNode *s=SearchBST(T,key);
if(s!=NULL)
{
printf("\n關鍵字%d已存在!\n",s->key);
return T;
}
s=(BTNode *)malloc(sizeof(BTNode)); /*生成一個結點空間*/
if(s==NULL)
printf("Error\n");
s->key=key;
s->lchild=s->rchild=NULL;
T=InsertBST(T,s); /*在二叉排序樹上插入該結點*/
return T;
}
/*二叉排序樹的建立*/
BSTree CreateBST() /*由空樹開始,輸入關鍵字序列,建立一棵二叉排序樹*/
{
BSTree T=NULL,s=NULL;
int key;
printf("輸入關鍵字序列,輸入-1結束\n");
while(1)
{
scanf("%d",&key);
if(key==-1)
break;
s=(BTNode *)malloc(sizeof(BTNode)); /*生成一個結點空間*/
s->key=key;
s->lchild=s->rchild=NULL;
T=InsertBST(T,s); /*在二叉排序樹中插入該結點*/
}
return T;
}
/*為了刪除操作而修改的二叉排序樹查詢*/
BTNode *SearchBST_F(BSTree T,int key,BSTree *F)
{ /*T指向根結點,*F儲存指向key的雙親的指標*/
/*查詢成功,返回指向key的記錄指標,查詢失敗返回NULL*/
if(T==NULL)
return NULL;
if(key==T->key)
return T;
*F=T;
if(key<T->key)
return SearchBST_F(T->lchild,key,F);
if(key>T->key)
return SearchBST_F(T->rchild,key,F);
}
/*二叉排序樹的刪除*/
BSTree DeleteBST(BSTree T,BTNode *p,BTNode *f)
{/*刪除p指標指向的結點,f指向*p的雙親結點*/
/*T是指向根結點的指標*/
BTNode *par,*s;
int kind;
if(p->lchild==NULL&&p->rchild==NULL) /*情況1,*p為葉子結點*/
kind=1;
else if(p->rchild==NULL) /*情況2,*p只有左子樹*/
kind=2;
else if(p->lchild==NULL) /*情況3,*p只有右子樹*/
kind=3;
else /*情況4,*p左右子樹非空*/
kind=4;
switch(kind)
{
case 1: if(f==NULL) /*p指向根結點,樹中只有根結點*/
T=NULL; /*刪除結點*p,T變空樹*/
else
{
if(f->lchild==p) /* *p是*f的左孩子*/
f->lchild=NULL;
else /* *p是*f的右孩子*/
f->rchild==NULL;
}
free(p); /*刪除結點釋放空間*/
break;
case 2: if(f==NULL) /*f為NULL,*p為根結點,且只有左子樹*/
T=p->lchild;
else
{
if(f->lchild==p) /* *p是*f的左子樹*/
f->lchild=p->lchild;
else /* *p是*f的右子樹*/
f->rchild=p->lchild;
}
free(p); /*刪除結點釋放空間*/
break;
case 3: if(f==NULL) /*f為NULL,*p為根結點,且只有左子樹*/
T=p->rchild;
else
{
if(p==f->lchild)
f->lchild=p->rchild;
else
f->rchild=p->rchild;
}
free(p);
break;
case 4: par=p;
s=p->lchild;
while(s->rchild!=NULL) /*找到p左子樹的關鍵字最大(最右下)的結點*/
{
par=s;
s=s->rchild;
}
p->key=s->key; /*s結點的值覆蓋p結點的值*/
if(par==p) /*處理特殊情況,*p的左孩子為*s*/
par->lchild=s->lchild;
else
par->rchild=s->lchild; /*s的左子樹連線到s的雙親結點作為雙親結點的右子樹*/
free(s); /*注意不是free(p),因為p的關鍵字被s的關鍵字覆蓋,s已無用*/
}
return T;
}
BSTree SearchDeleteBST(BSTree T,int key)
{
BTNode *f=NULL,*p=NULL;
p=SearchBST_F(T,key,&f); /*查詢key,p指向key,f指向雙親*/
if(p!=NULL)
T=DeleteBST(T,p,f);
else
printf("關鍵字為key的記錄不存在!\n");
return T;
}
int main()
{
return 0;
}