二叉排序樹(BST)構造與應用
本文取自《數據結構與算法》(C語言版)(第三版),出版社是清華大學出版社。
在VS2010中新建C++ Win32 控制檯應用程序項目,創建結果截圖:
二叉排序樹(BST):又稱二叉查找樹,其定義爲:二叉排序樹或者是空樹,或者是滿足以下性質的二叉樹。
(1) 若它的左子樹非空,則左子樹上所有記錄的關鍵字均小於根記錄的值。
(2) 若它的右子樹非空,則右子樹上所有記錄的關鍵字均大於根記錄的值。
(3) 左、右子樹本身又各是一棵二叉排序樹。
按中序遍歷BST所得到的中序序列是一個遞增有序序列。
二叉排序樹的類型定義:
typedef struct BSTNode
{
KeyType key; //數據域
BSTNode *lchild;
BSTNode *rchild;
}
1.二叉排序樹的插入操作(1)如果二叉排序樹T爲空,則創建一個關鍵字爲k的結點,將其作爲根結點。
(2)否則將k和根結點的關鍵字進行比較,如果相等則返回,如果k小於根結點的關鍵字則插入根結點的左子樹中,否則插入根結點的右子樹中。
二叉排序樹的插入算法:
int InsertBST(BSTNode *p, KeyType k)
{
if(p==NULL)
{
p=(BSTNode*)malloc(sizeof(BSTNode));
p->key=k;
p->lchild=p->rchild=NULL;
return 1;
}
else if(k==p->key)
return 0;
else if(k<p->key)
return InsertBST(p->lchild, k);
else
return InsertBST(p->rchild, k);
}
二叉排序樹的生成算法: BSTNode *CreateBST(KeyType A[], int n)
{
BSTNode *bt=NULL;
int i=0;
while(i<n)
{
InsertBST(bt, A[i]);
i++;
}
return bt;
}
示例:輸入{50,16,56,52,8}生成二叉排序樹
2.二叉排序樹的查找操作
首先將需要查找的值與根結點比較,如果相等則查找成功,算法終止;如果比根結點小則左子樹中查找,如果比根結點大則到右子樹查找。
二叉排序樹的查找算法的遞歸形式
BSTree SearchBST(BSTree t, int k)
{
if(t==null || k==t->key)
return t;
else if(k<t->key)
return SearchBST(t->lchild, k);
else
return SearchBST(t->rchild, k);
}
二叉排序樹的查找算法的非遞歸形式 BSTree SearchBST2(BSTree t, int k)
{
BSTree p=t;
while(p!=null && p->key!=k)
{
if(k<p->key)
p=p->lchild;
else
p=p->rchild;
}
return p;
}
查找過程演示圖:
3.二叉排序樹的刪除操作
刪除二叉排序樹的某一個結點的步驟如下:1)查找待刪除的結點
查找結點時,令*p指向其訪問到的結點,*f指向其雙親結點。若樹中找不到被刪結點時返回NULL,否則被刪除結點是*p,返回*p。
2)刪除結點
假設要刪除二叉排序樹中的一個結點*p,其雙親結點爲*f,則刪除結點*p時,需考慮以下3種情況:
(1)*p爲葉子結點。
在這種情況下,可以將*p結點直接刪除。
p爲左子樹:
f->lchild=NULL;
free(p);
p爲右子樹:
f->rclild=NULl;
free(p);
操作示意圖如下:
(2)*p只有左子樹,或只有右子樹。
對於這種情況,可以直接將*p的左子樹或右子樹與其雙親結點*f相連,然後刪除*p。
p爲f的左孩子,p的左子樹非空:
f->lchild=p->lchild;
free(p);
p爲f的左孩子,p的右子樹非空:
f->lchild=p->rchild;
free(p);
p爲f的右孩子,p的左子樹非空:
f->rchild=p->lchild;
free(p);
p爲f的右孩子,p的右子樹非空:
f->rchild=p->rchild;
free(p);
操作示意圖如下:
(3)*p有左右子樹。
方法一:設*s爲*p結點在中序序列中的直接前驅,將*p的左子樹改爲*f的左子樹,將*p的右子樹改爲*s的右子樹。
f->lchild=p->lchild;
s->rchild=p->rchild;
free(p);
操作示意圖如下:
方法二:用*p結點在中序序列中的直接前驅(或後繼)*s代替*p,然後再從二叉排序樹中將*s刪除。這時如果*s爲*p的直接前驅,則*s只有左子樹(或者沒有孩子),則刪除*s可以按照刪除*p的其餘兩種情況處理。如果*s爲*p的直接後繼,則*s只有右子樹(或者沒有孩子),刪除*s同理可以按照刪除*p的其餘兩種情況處理。
附錄
A.二叉排序樹的構造算法:
注:判定一棵二叉樹是二叉排序樹可以採用中序遍歷算法將樹上的頂點輸出,如果得到的中序序列是有序的,則說明這棵二叉樹是二叉排序樹,否則不是二叉排序樹。 #include<stdio.h>
#include<stdlib.h>
#define MAX 100
typedef struct tnode
{
int data;
struct tnode *lchild, *rchild;
}TNODE;
void create();
void insert(int m); //插入二叉排序樹的結點
void inOrder(TNODE *ptr); //中序遍歷
TNODE *root=NULL;
void inOrder(TNODE *ptr)
{
if(ptr!=NULL)
{
inOrder(ptr->lchild);
printf("%d ", ptr->data);
inOrder(ptr->rchild);
}
}
void create()
{
int n, i;
int k[MAX];
printf("Please input the node number:\n");
scanf("%d", &n);
for(i=0; i<n; i++)
scanf("%d",&k[i]);
for(i=0; i<n; i++)
insert(k[i]);
}
void insert(int m)
{
TNODE *p1, *p2;
if(root==NULL)
{
root=(TNODE *)malloc(sizeof(TNODE));
root->data=m;
root->lchild=root->rchild=NULL;
}
else
{
p1=root;
while(m!=p1->data)
{
if((m<p1->data) && (p1->lchild!=NULL))
p1=p1->lchild;
else if((m>p1->data) && (p1->rchild!=NULL))
p1=p1->rchild;
else if((m<p1->data) && (p1->lchild==NULL))
{
p2=(TNODE *)malloc(sizeof(TNODE));
p2->data=m;
p2->lchild=p2->rchild=NULL;
p1->lchild=p2;
return;
}
else if((m>p1->data) && (p1->rchild==NULL))
{
p2=(TNODE *)malloc(sizeof(TNODE));
p2->data=m;
p2->lchild=p2->rchild=NULL;
p1->rchild=p2;
return;
}
}
}
}
int main()
{
create();
printf("\n");
inOrder(root);
printf("\n");
return 0;
}
Ctrl+F5運行SortTree.cpp輸出結果如下:
B.求出二叉排序樹T中小於x的最大元素和大於x的最小元素
在二叉排序樹中,一個小於樹中某個結點的最大元素,是在中序序列中這個結點的直接前驅;大於這個結點的最小元素,是在中序序列中這個結點的直接後繼。
其程序如下:
#include<stdio.h>
#include<stdlib.h>
#define MAX 100
typedef struct tnode
{
int data;
struct tnode *lchild, *rchild;
}TNODE;
int last=0;
void create();
void insert(int m); //插入二叉排序樹的結點
void findMaxMin(int aim, TNODE *ptr);
TNODE *root=NULL;
void findMaxMin(int aim, TNODE *ptr)
{
if(ptr!=NULL)
{
findMaxMin(aim, ptr->lchild);
if(last<aim && ptr->data>=aim) //找到小於aim的最大元素
printf("a=%d\n",last);
if(last<=aim && ptr->data>aim) //找到大於aim的最小元素
printf("b=%d\n",ptr->data);
last=ptr->data;
findMaxMin(aim, ptr->rchild);
}
}
void create()
{
int n, i;
int k[MAX];
printf("Please input the node number:\n");
scanf("%d", &n);
for(i=0; i<n; i++)
scanf("%d",&k[i]);
for(i=0; i<n; i++)
insert(k[i]);
}
void insert(int m)
{
TNODE *p1, *p2;
if(root==NULL)
{
root=(TNODE *)malloc(sizeof(TNODE));
root->data=m;
root->lchild=root->rchild=NULL;
}
else
{
p1=root;
while(m!=p1->data)
{
if((m<p1->data) && (p1->lchild!=NULL))
p1=p1->lchild;
else if((m>p1->data) && (p1->rchild!=NULL))
p1=p1->rchild;
else if((m<p1->data) && (p1->lchild==NULL))
{
p2=(TNODE *)malloc(sizeof(TNODE));
p2->data=m;
p2->lchild=p2->rchild=NULL;
p1->lchild=p2;
return;
}
else if((m>p1->data) && (p1->rchild==NULL))
{
p2=(TNODE *)malloc(sizeof(TNODE));
p2->data=m;
p2->lchild=p2->rchild=NULL;
p1->rchild=p2;
return;
}
}
}
}
int main()
{
int toBeFind;
create();
printf("\n");
printf("Input the record to be finded! \n");
scanf("%d", &toBeFind);
findMaxMin(toBeFind, root);
printf("\n");
return 0;
}
Ctrl+F5運行SortTree1.cpp輸出結果如下: