實驗說明
數據結構實驗三 二叉樹的實驗——二叉樹的主要遍歷算法
一、實驗目的
通過本實驗使學生熟悉二叉樹遍歷的各種算法;掌握采用遞歸實現二叉樹遍歷算法的方法;深刻理解棧在遞歸中的作用,進而學會遞歸轉為非遞歸的方法;特別訓練學生在編程上控制復雜結構的能力,為今后控制更為復雜結構,進而解決有一定難度的復雜問題奠定基礎。
二、實驗內容
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)嘗試在順序存儲結構上設計這些遍歷算法,並在時空效率上與鏈式存儲進行分析對比,並得出結論(僅是分析得出結論即可,可以不實現):鏈表法時間復雜度較高,空間復雜度較低;數組法時間復雜度較低,空間復雜度較高。因為數組法一開始就定義好樹的大小,如果有空節點就浪費了空間,而鏈表法不會創建空結點,因此數組法的空間復雜度較高。鏈表法對指針的操作較繁瑣,所需時間長,因此鏈表法的時間復雜度較低。