1. 程式人生 > 實用技巧 >資料結構 實驗報告(三)二叉樹

資料結構 實驗報告(三)二叉樹

實驗說明

資料結構實驗三 二叉樹的實驗——二叉樹的主要遍歷演算法

一、實驗目的

通過本實驗使學生熟悉二叉樹遍歷的各種演算法;掌握採用遞迴實現二叉樹遍歷演算法的方法;深刻理解棧在遞迴中的作用,進而學會遞迴轉為非遞迴的方法;特別訓練學生在程式設計上控制複雜結構的能力,為今後控制更為複雜結構,進而解決有一定難度的複雜問題奠定基礎。

二、實驗內容

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)嘗試在順序儲存結構上設計這些遍歷演算法,並在時空效率上與鏈式儲存進行分析對比,並得出結論(僅是分析得出結論即可,可以不實現):連結串列法時間複雜度較高,空間複雜度較低;陣列法時間複雜度較低,空間複雜度較高。因為陣列法一開始就定義好樹的大小,如果有空節點就浪費了空間,而連結串列法不會建立空結點,因此陣列法的空間複雜度較高。連結串列法對指標的操作較繁瑣,所需時間長,因此連結串列法的時間複雜度較低。