資料結構 實驗報告(三)二叉樹
實驗說明
資料結構實驗三 二叉樹的實驗——二叉樹的主要遍歷演算法
一、實驗目的
通過本實驗使學生熟悉二叉樹遍歷的各種演算法;掌握採用遞迴實現二叉樹遍歷演算法的方法;深刻理解棧在遞迴中的作用,進而學會遞迴轉為非遞迴的方法;特別訓練學生在程式設計上控制複雜結構的能力,為今後控制更為複雜結構,進而解決有一定難度的複雜問題奠定基礎。
二、實驗內容
1.程式設計實現前、中、後序的遞迴與非遞迴演算法(共六個演算法)。特別要求:設計並實現構造二叉樹鏈式儲存的演算法。
2.進一步提供如下演算法:
(1)設計與實現層序遍歷的遞迴與非遞迴演算法;
(2)提供另一種構造二叉樹鏈式儲存的演算法;
(3)提供另外一種後序非遞迴遍歷的實現演算法。
實驗報告
1.實現功能描述
程式設計實現前、中、後序的遞迴與非遞迴演算法(共六個演算法)。特別要求:設計並實現構造二叉樹鏈式儲存的演算法。
2.方案比較與選擇
(1)可以使用棧和佇列來實現非遞迴演算法。因為棧的功能足以完成題目要求,所以使用棧來實現。
3.設計演算法描述
(1)定義一個結構體代表結點,其中包含資料域data和左右孩子。
(2)設計棧。
(3)進行模組劃分,給出功能組成框圖。形式如下:
(4)基本功能模組:
①前序建立並輸入二叉樹
②前序遍歷二叉樹
③中序遍歷二叉樹
④後序遍歷二叉樹
(5)用流程圖描述關鍵演算法:
4.演算法實現(即完整源程式,帶註解)
(1)遞迴演算法:
點選檢視詳細內容
/* 程式設計實現前、中、後序的遞迴與非遞迴演算法(共六個演算法)。 特別要求:設計並實現構造二叉樹鏈式儲存的演算法。 ABDH//I///CF/K//G// AB/EJ///CF/M//G// */ #include<stdio.h> #include<stdlib.h> #define ERR 1 #define OK 0 typedef struct BTNode { char data; struct BTNode* lchild, * rchild; }BTNode, * BT; int CreateBT(BT* T); int PreOrderTraverse(BT T); int InOrderTraverse(BT T); int PostOrderTraverse(BT T); int main(void) { BT bt = NULL; printf("請按前序輸入二叉樹(輸入/代表空):"); CreateBT(&bt); printf("\n二叉樹前序遍歷:"); PreOrderTraverse(bt); printf("\n二叉樹中序遍歷:"); InOrderTraverse(bt); printf("\n二叉樹後序遍歷:"); PostOrderTraverse(bt); return OK; } //前序建立二叉樹 int CreateBT(BT* T) { char data; scanf("%c", &data); if (data == '/') *T = NULL; else { *T = (BT)malloc(sizeof(BTNode)); if (!*T) { printf("error:CreateBT"); return ERR; } (*T)->data = data; CreateBT(&(*T)->lchild); CreateBT(&(*T)->rchild); } return OK; } //二叉樹前序遍歷 int PreOrderTraverse(BT T) { if (T != NULL) { printf("%c ", T->data); PreOrderTraverse(T->lchild); PreOrderTraverse(T->rchild); } return OK; } //二叉樹中序遍歷 int InOrderTraverse(BT T) { if (T != NULL) { InOrderTraverse(T->lchild); printf("%c ", T->data); InOrderTraverse(T->rchild); } return OK; } //二叉樹後序遍歷 int PostOrderTraverse(BT T) { if (T != NULL) { PostOrderTraverse(T->lchild); PostOrderTraverse(T->rchild); printf("%c ", T->data); } return OK; }
(2)非遞迴演算法:
點選檢視詳細內容
/*
程式設計實現前、中、後序的遞迴與非遞迴演算法(共六個演算法)。
特別要求:設計並實現構造二叉樹鏈式儲存的演算法。
ABDH//I///CF/K//G//
AB/EJ///CF/M//G//
*/
#include<stdio.h>
#include<stdlib.h>
#define ERR 1
#define OK 0
typedef struct BTNode {
char data;
struct BTNode* lchild, * rchild;
} BTNode;
typedef struct Stack {
BTNode* bt;
int top;
int base;
int stacksize;
} Stack;
BTNode* CreatBT(BTNode* BT);
void InOrderTraverse(BTNode* BT);
void PreOrderTraverse(BTNode* BT);
void PostOrderTraverse(BTNode* BT);
void InitStack(Stack* S);
void GetTop(Stack S, BTNode* e);
void Pop(Stack* S, BTNode* e);
void Push(Stack* S, BTNode e);
int StackEmpty(Stack* S);
int main(void) {
BTNode* BT = NULL;
printf("請按前序輸入二叉樹(輸入/代表空):");
BT = CreatBT(BT);
printf("\n二叉樹前序遍歷:");
PreOrderTraverse(BT);
printf("\n二叉樹中序遍歷:");
InOrderTraverse(BT);
printf("\n二叉樹後序遍歷:");
PostOrderTraverse(BT);
return OK;
}
//前序建立二叉樹
BTNode* CreatBT(BTNode* BT) {
char data;
scanf("%c", &data);
if (data == '/') BT = NULL;
else {
BT = (BTNode*)malloc(sizeof(BTNode));
BT->data = data;
BT->lchild = CreatBT(BT->lchild);
BT->rchild = CreatBT(BT->rchild);
}
return BT;
}
//二叉樹前序遍歷
void PreOrderTraverse(BTNode* BT) {
BTNode* bt = BT;
Stack S;
InitStack(&S);
do {
while (bt != NULL) {
printf("%c ", bt->data);
Push(&S, *bt);
bt = bt->lchild;
}
if (S.top != 0) {
bt = (BTNode*)malloc(sizeof(BTNode));
Pop(&S, bt);
bt = bt->rchild;
}
} while (!StackEmpty(&S) || (bt != NULL));
}
//二叉樹中序遍歷
void InOrderTraverse(BTNode* BT) {
BTNode* bt = BT;
Stack S;
InitStack(&S);
while (bt || !StackEmpty(&S)) {
if (bt) {
Push(&S, *bt);
bt = bt->lchild;
}
else {
bt = (BTNode*)malloc(sizeof(BTNode));
Pop(&S, bt);
printf("%c ", bt->data);
bt = bt->rchild;
}
}
}
//二叉樹後序遍歷
void PostOrderTraverse(BTNode* BT){
BTNode* bt = BT;
//用於儲存上個已使用過的結點
BTNode* lastnode = BT;
Stack S;
InitStack(&S);
while (bt || !StackEmpty(&S)) {
if (bt) {
Push(&S, *bt);
bt = bt->lchild;
}
else {
bt = (BTNode*)malloc(sizeof(BTNode));
Pop(&S, bt);
if (bt->rchild && bt->rchild->data != lastnode->data) {
Push(&S, *bt);
bt = bt->rchild;
Push(&S, *bt);
bt = bt->lchild;
}
//右子樹為空或者右子樹已經被訪問
else {
printf("%c ", bt->data);
//lastnode儲存結點
lastnode = bt;
bt = NULL;
}
}
}
}
//棧的初始化
void InitStack(Stack* S) {
S->bt = (BTNode*)malloc(sizeof(BTNode) * 100);
S->top = 0;
S->base = 0;
S->stacksize = 100;
}
//獲取棧頂元素
void GetTop(Stack S, BTNode* e) {
if (S.top == S.base) {
return;
}
*e = S.bt[S.top];
}
//彈棧
void Pop(Stack* S, BTNode* e) {
if (S->top == S->base) {
return;
}
*e = S->bt[--S->top];
}
//壓棧
void Push(Stack* S, BTNode e) {
//如果棧滿
if (S->top - S->base >= S->stacksize) {
return;
}
S->bt[S->top++] = e;
}
//判斷棧是否為空
int StackEmpty(Stack* S) {
if (S->top == S->base) {
return ERR;
}
else {
return OK;
}
}
5.實驗結果測試與分析
(1)資料測試程式截圖
(2)對結果進行分析:
①前序遍歷正確
②中序遍歷正確
③後序遍歷正確
④棧執行正常
6.思考及學習心得
(1)描述實驗過程中對此部分知識的認識:
(2)特別描述在學習方法上的收穫及體會;
(3)針對前面的思考題內容在此回答。
1)實現了棧的,更進一步理解和掌握棧的的使用。
2)這次的實驗,鞏固了我的程式設計模組化的思想。模組化降低了程式的耦合性,提高了程式的內聚性;降低了程式複雜度,使程式設計、除錯和維護等操作簡單化。模組化使得程式設計更加簡單和直觀,從而提高了程式的易讀性和可維護性,而且還可以把程式中經常用到的一些計算或操作編寫成通用函式,以供隨時呼叫。
3)嘗試在順序儲存結構上設計這些遍歷演算法,並在時空效率上與鏈式儲存進行分析對比,並得出結論(僅是分析得出結論即可,可以不實現):連結串列法時間複雜度較高,空間複雜度較低;陣列法時間複雜度較低,空間複雜度較高。因為陣列法一開始就定義好樹的大小,如果有空節點就浪費了空間,而連結串列法不會建立空結點,因此陣列法的空間複雜度較高。連結串列法對指標的操作較繁瑣,所需時間長,因此連結串列法的時間複雜度較低。