一.課程設計題目及要求
(一)課程設計題目
用順序和二叉鏈表作存儲結構實現二叉排序樹:
(1)以回車('\n')為輸入結束標志,輸入數列L,生成一棵二叉排序樹T;
(2)對二叉排序樹T作中序遍歷,輸出結果;
(3)輸入元素x,查找二叉排序樹T,若存在含x的結點,則刪除該結點,并作中序遍歷(執行操作2);否則輸出信息“無x”。
(二)課程設計要求
在處理題目時,要求從分析題目的需求入手,按設計抽象數據類型、構思算法、通過設計實現抽象
數據類型、編制上機程序和上機調試等若干步驟完成題目,最終寫出完整的分析報告。對于稍復雜
的程序設計,要充分利用模塊化程序設計方法,自頂向下,逐步細化,在整體思路確定的情況下,
考慮所需模塊數,各模塊完成功能以及模塊之間的數據聯系和調用關系。給出主要模塊的算法描
述,用流程圖或偽代碼表示。說明:在設計的過程中,步驟1---步驟4往往是反復進行, 在后續步
驟中發現問題,往往需要從頭重新分析、設計。
二.算法思想
(一)二叉排序樹的定義
二叉排序樹的定義:二叉排序樹或者是一棵空樹, 或者是一棵具有如下性質的二叉樹:
(1)每個結點都有一個作為搜索依據的關鍵碼(key),所有結點的關鍵碼互不相同;
(2)若它的左子樹非空,則左子樹上所有結點的值均小于根結點的值;
(3)若它的右子樹非空,則右子樹上所有結點的值均大于根結點的值;
(4)左、右子樹本身又各是一棵二叉排序樹
(二)二叉排序樹的實現
1.建立二叉排序樹
建二叉樹的結點至少應當包含三個域,分別存放結點的數據data,左子女結點指針leftChild和右子女結點指針rightChild。整個二叉樹的鏈表要有一個表頭指針,它指向二叉樹的根結點,其作用是當作樹的訪問點
從空的二叉排序樹開始,經過一系列的查找插入操作以后,生成了一棵二叉排序樹。
根據二叉排序樹的定義,建立一棵二叉排序樹的過程是按照待排序序列元素的先后次序,不斷動態生成二叉樹的結點,逐個插入到二叉樹中。若p為根結點指針,b為當前待插入元素,其過程可以描述為:
若為空樹(p=NULL),動態生成一個結點,其數據域為當前待插入元素b,左、右指針域為“空”,p指向該結點。若非空樹,比較b與根結點數據data(p)
如果b<data(p),將b插入左子樹中;
如果b≥data(p),將b插入右子樹中;
左、右子樹的插入方式與二叉排序樹的插入方式相同。
不斷調用上述的插入過程,直到所有待排序序列均排入后,就形成一棵二叉排序樹。
由此可見,建立二叉排序樹就是多次調用二叉排序樹的插入算法。
2.二叉排序樹的中序遍歷
中序遍歷二叉樹算法的框架是:
若二叉樹為空,則空操作;
否則 中序遍歷左子樹(L);
訪問根結點(V);
中序遍歷右子樹(R)。
中序遍歷二叉樹也采用遞歸函數的方式,先訪問左子樹,然后訪問根結點,最后訪問右子樹.直至所有的結點都被訪問完畢
3.二叉排序樹中元素的查找
在二叉排序樹上進行查找,是一個從根結點開始,沿某一個分支逐層向下進行比較判等的過程。它
可以是一個遞歸的過程。假設我們想要在二叉排序樹中查找關鍵碼為x的元素,查找過程從根結點
開始。如果根指針為NULL,則查找不成功;否則用給定值x與根結點的關鍵碼進行比較;如果給定
值等于根結點的關鍵碼,則查找成功,返回查找成功的信息,并報告查找到的結點地址。如果給定
值小于根結點的關鍵碼,則繼續遞歸查找根結點的左子樹;否則,遞歸搜索根結點的右子樹。
4.二叉排序樹中元素的查找
對于二叉排序樹,刪去樹上的一個結點相當于刪去有序序列中的一個記錄,只要在刪除某個結點之
后依舊保持二叉排序樹的特性即可。假設在二叉排序樹上被刪除結點為*p(指向結點的指針是
p),其雙親結點為*f(結點指針為f),且不失一般性,可設*p是*f的左孩子,若*p結點為葉子結
點,即p和l均為空,只需修改其雙親結點指針即可。若*p結點只有左子樹或者只有右子樹,只要令
左子樹或右子樹直接成為其雙親結點即可。若左子樹和右子樹都不為空,令*p的直接前驅替代*p,
然后從二叉排序樹中刪除它的直接前驅,即可。
三.算法思想
(一)程序設計思想
程序主要設計了四個功能:首先是創建二叉排序樹,完成后出現任務菜單,以數字代碼確定,二叉
排序樹的中序遍歷和查找,刪除步驟,最后完成,結束。
(二)程序模塊
1.二叉鏈表結構(BST),C
#include
#include
typedef int ElementType;
typedef struct TNode *Position;
typedef Position BinTree;
struct TNode{
ElementType Data;
BinTree Left;
BinTree Right;
};
void PreorderTraversal( BinTree BST );
void InorderTraversal( BinTree BST );
BinTree Insert( BinTree BST, ElementType X);
BinTree Delete( BinTree BST, ElementType X);
Position Find( BinTree BST, ElementType X);
Position FindMin( BinTree BST );
Position FindMax( BinTree BST );
void solve( BinTree BST );
int calculateASL( BinTree BST,double*s,double *j,int i );
int main()
{
BinTree BST, MinP, MaxP, Tmp;
ElementType X;
int number,N, i;
BST = NULL;
scanf("%d", &N);
for ( i=0; i
scanf("%d", &X);
BST = Insert(BST, X);
}
printf("Preorder:"); PreorderTraversal(BST);printf("\n");
/*MinP = FindMin(BST);
MaxP = FindMax(BST);
scanf("%d", &N);
for( i=0; i
scanf("%d", &X);
Tmp = Find(BST, X);
if (Tmp == NULL) printf("%d is not found\n", X);
else {
printf("%d is found\n", Tmp->Data);
if (Tmp==MinP) printf("%d is the smallest key\n",Tmp->Data);
if (Tmp==MaxP) printf("%d is the largest key\n",Tmp->Data);
}
}
scanf("%d", &N);
for( i=0; i
scanf("%d", &X);
BST = Delete(BST, X);
}*/
printf("Inorder:"); InorderTraversal(BST);printf("\n");
double s=0,j=0;
int k=0;
calculateASL(BST,&s,&j,k);
printf("ASL=%0.4f\n",s/j);
solve(BST);
return 0;
}
void PreorderTraversal( BinTree BST )
{
if( BST ) {
printf(" %d", BST->Data);
PreorderTraversal( BST->Left );
PreorderTraversal( BST->Right );
}
}
void InorderTraversal( BinTree BST )
{
if( BST ) {
InorderTraversal( BST->Left );
printf(" %d", BST->Data);
InorderTraversal( BST->Right );
}
}
Position Find( BinTree BST, ElementType X )
{
if( !BST ) return NULL; /*查找失敗*/
if( X > BST->Data ) {
return Find(BST->Right,X );/*在右子樹中.繼續查找*/
}
else if( X < BST->Data ){
return Find( BST->Left,X ); /*在左子樹中繼續查找*/
}
else /* X == BST->Data */
return BST; /*查找成功,返回結點的找到結點的地址*/
}
Position FindMin( BinTree BST )
{
if(BST){
while(BST->Left){
BST=BST->Left;
}
}
return BST;
}
Position FindMax( BinTree BST )
{
if(BST ){
while( BST->Right ){
BST = BST->Right;
}/*沿右分支繼續查找,直到最右葉結點*/
}
return BST;
}
BinTree Insert( BinTree BST, ElementType X)
{
if( !BST ){ /*若原樹為空,生成并返回一個結點的二叉搜索樹*/
BST = (BinTree)malloc(sizeof(struct TNode));
BST->Data = X;
BST->Left = NULL;
BST->Right = NULL;
}
else { /*開始找要插入元素的位置*/
if( X < BST->Data )
BST->Left = Insert( BST->Left, X );/*遞歸插入左子樹*/
elseif( X > BST->Data )
BST->Right = Insert( BST->Right,X ); /*遞歸插入右子樹*/
/* else X已經存在,什么都不做*/
}
return BST;
}
BinTree Delete( BinTree BST, ElementType X) {
BinTree head=BST;
BinTree t=Find(BST,X);
if(!t) {
printf("Not Found\n");
return BST;
}
BinTree m=FindMax(t->Left);
if(m) {//左子樹處理
t->Data=m->Data;
if(t->Left==m)t->Left=m->Left;
else {
t=t->Left;
while(t->Right!=m)t=t->Right;
t->Right=m->Left;
}
}else {
m=FindMin(t->Right);
if(m) {//左子樹為空時
t->Data=m->Data;
if(t->Right==m)t->Right=m->Right;
else {
t=t->Right;
while(t->Left!=m)t=t->Left;
t->Left=m->Right;
}
} else { //當其為葉節點時
if(BST==t)return NULL;
while(BST) {
if(BST->Data>=t->Data){
if(BST->Left==t)BST->Left=NULL;
BST=BST->Left;
} else {
if(BST->Right==t)BST->Right=NULL;
BST=BST->Right;
}
}
}
}
// free(t);
return head;
}
void solve( BinTree BST )
{
ElementType X;
Position tmp;
printf("which element will you delete:");
scanf("%d",&X);
tmp = Find( BST,X );
if(tmp ==NULL)printf("thereis not X!\n");
else{
BST = Delete( BST, X );
InorderTraversal(BST);
}
}
int calculateASL(BinTree BST,double
*s,double *j,int i) /*計算平均查找長度*/
{
if(BST){
i++;
*s=*s+i;
if(calculateASL(BST->Left,s,j,i)){
(*j)++;
if(calculateASL(BST->Right,s,j,i)){
i--;
return(1);
}
}
}
else return(1);
2.順序存儲(BST),C
#include"stdio.h"
#include"malloc.h"
#include"windows.h"
#define endflag 999999
#define N 1000
int b[N];
typedef struct
{
intdata;
intother;
intflag1;
}Link;
void Build(Link a[N])
{
intw,i,j,k;
for(i=0;i<=N;i++){
a[i].flag1=0;
a[i].data=0;
}
printf("\n\t\t\t請輸入樹的根結點:");
scanf("%d",&a[1].data);
a[1].flag1=1;
printf("\n\t\t\t請輸入結點個數:");
scanf("%d",&k);
for(j=1;j<=k;j++){
printf("\n\t\t\t請輸入結點的數值:");
scanf("%d",&w);
printf("\n\t\t\t第%d位: %d",j,w);
a[0].data=w;
a[0].flag1=1;
i=1;
if(a[0].data
loop:if(a[2*i].flag1==0){
a[2*i].data=a[0].data;
a[2*i].flag1=a[0].flag1;
}
if(a[2*i].flag1==1){
i=2*i;
if(a[0].data
if(a[0].data>a[i].data)goto loop1;
}
}
if(a[0].data>a[i].data){
loop1:if(a[2*i+1].flag1==0){
a[2*i+1].data=a[0].data;
a[2*i+1].flag1=a[0].flag1;
}
if(a[2*i+1].flag1==1){
i=2*i+1;
if(a[0].data
gotoloop;
if(a[0].data>a[i].data)
gotoloop1;
}
}
}
}
void Sdel(Link a[N])
{
inti,flag=0,q,number=1,j=1;
printf("\n\t\t\t請輸入需要刪除的結點的數值:");
scanf("%d",&q);
for(i=1;i<=N;i++)
if(a[i].data==q){
a[i].data=0;
printf("\n\t\t\t已刪除%d: ",q);
flag=1;
for(i=1;i<=N;i++){
if(a[i].data!=0){
b[j]=a[i].data;
j++;
number++;
}
}
for(i=1;i<=N;i++){
a[i].flag1=0;
a[i].data=0;
}
a[1].data=b[1];
a[1].flag1=1;
for(j=2;j<=number-1;j++){
a[0].data=b[j];
a[0].flag1=1;
i=1;
if(a[0].data
loop:if(a[2*i].flag1==0){
a[2*i].data=a[0].data;
a[2*i].flag1=a[0].flag1;
}
if(a[2*i].flag1==1){
i=2*i;
if(a[0].data
if(a[0].data>a[i].data)goto loop1;
}
}
if(a[0].data>a[i].data){
loop1:if(a[2*i+1].flag1==0){
a[2*i+1].data=a[0].data;
a[2*i+1].flag1=a[0].flag1;
}
if(a[2*i+1].flag1==1){
i=2*i+1;
if(a[0].data
if(a[0].data>a[i].data)goto loop1;
}
}
}
printf("\n\t\t\t顯示已經刪除結點后的數據\n");
for(i=1;i
if(a[i].data!=0){
printf("\n\t\t\t位于二叉排序樹的第%d位的數據為: ",i);
printf("->%d\n\n",a[i].data);
}
}
}
if(flag==0)printf("\n\n\n\t\t\t不存在該結點,不能刪除\n");
}
int main()
{
inti,flag=0,ch=0;
Linka[N];
printf("\n1:建立二叉排序樹并中序遍歷輸出");
printf("\n2:輸入一個元素,刪除后重構二叉排序樹并中序輸出");
while(ch==ch){
scanf("%d",&ch);
switch(ch){
case0:exit(0);
case1:printf("\t\t\t請輸入信息\n");
Build(a);
printf("\n");
for(i=1;i
if(a[i].data!=0){
printf("\n\t\t\t位于二叉排序樹的第%d位的數值為:",i);
printf("->%d\n\n",a[i].data);
}
}
case2:printf("輸入元素x,查找二叉排序樹T,若存在含x的結點,則刪除該結點并作中序遍歷,否則輸出無x:");
Sdel(a);
break;
default:
printf("無此結點\n");
break;
}
}
return0;
}
3.二叉鏈表(AVL),C
#include
#include
#include
#include
using namespace std;
typedef struct TreeNode *AvlTree;
typedef struct TreeNode *Position;
struct TreeNode
{
int Data;
AvlTree Left;
AvlTree Right;
int Height;
};
void PreorderTraversal( AvlTree AVL )
{
if( AVL ) {
printf("%d ", AVL->Data);
PreorderTraversal( AVL->Left );
PreorderTraversal( AVL->Right);
}
}
void InorderTraversal( AvlTree AVL )
{
if( AVL ) {
InorderTraversal( AVL->Left );
printf("%d ", AVL->Data);
InorderTraversal( AVL->Right);
}
}
int calculateASL(AvlTree AVL,double
*s,double *j,int i) /*計算平均查找長度*/
{
if(AVL){
i++;
*s=*s+i;
if(calculateASL(AVL->Left,s,j,i)){
(*j)++;
if(calculateASL(AVL->Right,s,j,i)){
i--;
return(1);
}
}
}
else return(1);
}
AvlTree Insert(int x, AvlTree T);//插入新節點,必要時調整
Position SingleRotateWithLeft(Positiona);//左單旋
Position SingleRotateWithRight(Positionb);//右單旋
Position DoubleRotateWithLeft(Positiona);//左右旋
Position DoubleRotateWithRight(Positionb);//右左旋
int Max(int x1, int x2);//返回兩個int中較大的
int Height(Position P);//返回一個節點的高度
int main()
{
int n, x;
AvlTree T = NULL;
scanf("%d", &n);
for (int i = 0; i < n; i++)
{
scanf("%d", &x);
T = Insert(x, T);
}
printf("Preorder:");
PreorderTraversal(T);printf("\n");
printf("Inorder:");
InorderTraversal(T);printf("\n");
///printf("%d\n", T->Data);//打印根節點的值
double s=0,j=0;
int k=0;
calculateASL(T,&s,&j,k);
printf("ASL=%0.4f\n",s/j);
return 0;
}
AvlTree Insert(int x, AvlTree T)
{
if (T == NULL)
{
T = (AvlTree)malloc(sizeof(struct TreeNode));
T->Data = x;
T->Left = T->Right = NULL;
T->Height = 0;
}
else if (x < T->Data)//向左子樹插入
{
T->Left = Insert(x, T->Left);
if (Height(T->Left) - Height(T->Right) == 2)//需調整
{
if (x < T->Left->Data)
T = SingleRotateWithLeft(T);
else
T = DoubleRotateWithLeft(T);
}
}
else if (x > T->Data)//向右子樹插入
{
T->Right = Insert(x, T->Right);
if (Height(T->Right) - Height(T->Left) == 2)//需調整
{
if (x >T->Right->Data)
T = SingleRotateWithRight(T);
else
T = DoubleRotateWithRight(T);
}
}
/*else值為x的節點已經存在樹中,無需插入*/
/*更新節點高度*/
T->Height = Max(Height(T->Left), Height(T->Right)) + 1;
return T;
}
Position SingleRotateWithLeft(Position a)
{
Position b = a->Left;
a->Left = b->Right;
b->Right = a;
//更新a, b節點高度
a->Height = Max(Height(a->Left), Height(a->Right)) + 1;
b->Height = Max(Height(b->Left), Height(b->Right)) + 1;
return b;/*新的根節點*/
}
Position SingleRotateWithRight(Position b)
{
Position a = b->Right;
b->Right = a->Left;
a->Left = b;
//更新a,b節點高度
a->Height = Max(Height(a->Left), Height(a->Right)) + 1;
b->Height = Max(Height(b->Left), Height(b->Right)) + 1;
return a;/*新的根節點*/
}
Position DoubleRotateWithLeft(Position a)
{
a->Left = SingleRotateWithRight(a->Left);
return SingleRotateWithLeft(a);
}
Position DoubleRotateWithRight(Position b)
{
b->Right = SingleRotateWithLeft(b->Right);
return SingleRotateWithRight(b);
}
int Max(int x1, int x2)
{
return (x1 > x2) ? x1 : x2;
}
int Height(Position P)
{
if (P == NULL)//空節點高度為-1
return -1;
return P->Height;
}
四.算法思想
(一)程序調試
(1)二叉鏈表存儲(BST):
(2)順序存儲(BST):
(3)二叉鏈表存儲(AVL)
(二)測試結果分析
輸入一串數據,進行中序遍歷,輸入元素x,查找二叉排序樹T,若存在含x的結點,則刪除該結點,并作
中序遍歷(執行操作2);否則輸出信息“無x”。驗證結果正確,說明其符合算法設計的要求:(1)正確
性、可讀性、健壯性、效率與低儲存量需求.要寫一個優質的算法,就必須考慮其時間復雜度(它
表示隨問題的規模n的增大,算法執行時間的增長率和f(n)的增長率相同)和空間復雜度。遍歷
二叉樹的算法中的基本操作是訪問結點,則不論按那一次次序進行遍歷,對含n個結點的二叉樹,
其時間復雜度為O(n)。所需輔助空間為遍歷過程中棧的最大容量,即樹的深度log(n),最壞情況
下為n,則空間復雜度也為O(n)。