實驗七 用順序和二叉鏈表作存儲結構實現二叉排序樹和平衡二叉樹

一.課程設計題目及要求

(一)課程設計題目

用順序和二叉鏈表作存儲結構實現二叉排序樹:

(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)。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,461評論 6 532
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,538評論 3 417
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,423評論 0 375
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,991評論 1 312
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,761評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,207評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,268評論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,419評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,959評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,782評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,983評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,528評論 5 359
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,222評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,653評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,901評論 1 286
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,678評論 3 392
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,978評論 2 374

推薦閱讀更多精彩內容

  • 課程介紹 先修課:概率統計,程序設計實習,集合論與圖論 后續課:算法分析與設計,編譯原理,操作系統,數據庫概論,人...
    ShellyWhen閱讀 2,329評論 0 3
  • -二叉搜索樹 查找問題:靜態查找和動態查找,靜態查找可以用二分查找-判定樹,那么針對動態查找數據如何組織?(樹的動...
    Spicy_Crayfish閱讀 1,368評論 0 2
  • 在學校最快樂的時光永遠是獨處的時候
    spectators閱讀 164評論 0 0
  • 也許沒有哪三個字,能如“心理學”一樣,被人賦予如此多的神秘色彩。一旦你跟別人提起自己正在學習心理學專業。若是對方含...
    可惜無聲閱讀 1,614評論 0 3
  • 本文參加#未完待續,就要表白#活動,本人承諾,文章內容為原創,且未在其他平臺發表過。 冷遇見了暖,就有了天氣;冬遇...
    噠噠啊dada閱讀 432評論 0 6