樹的學習——(遞迴構建二叉樹、遞迴非遞迴前序中序後序遍歷二叉樹、根據前序序列、中序序列構建二叉樹)
阿新 • • 發佈:2019-02-01
前言
最近兩個星期一直都在斷斷續續的學習二叉樹的資料結構,昨晚突然有點融匯貫通的感覺,這裡記錄一下吧題目要求
給定前序序列,abc##de#g##f###,構建二叉樹,並且用遞迴和非遞迴兩種方法去做前序,中序和後序遍歷二叉樹的資料結構
#define STACKSIZE 10005 /** * 二叉樹的資料結構 */ typedef struct btree { struct btree *lchild; struct btree *rchild; char item; } btree; typedef btree *bt; /** * 定義順序棧資料結構(非遞迴遍歷) */ typedef struct stack { btree *db[STACKSIZE]; int top; } stack;
遞迴構建二叉樹
構建二叉樹有固定的幾種考察型別:- 根據完整的先序序列構建二叉樹
- 根據前序和中序序列構建二叉樹
根據先序序列構建二叉樹(c語言實現)
char str[101] = "abc##de#g##f###"; int count = 0; /** * 根據先序序列構建二叉樹(因為涉及到對根節點指標修改,因此傳遞根節點指標的指標) */ void createBtree(btree **t) { if (str[count ++] == '#') { *t = NULL; } else { *t = (btree *)malloc(sizeof(btree)); (*t)->item = str[count - 1]; createBtree(&(*t)->lchild); createBtree(&(*t)->rchild); } }
遞迴的前序、中序、後序演算法(c語言實現)
/** * 遞迴先序遍歷二叉樹 */ void recPreorder(btree *t) { if (t) { printf("%c", t->item); recPreorder(t->lchild); recPreorder(t->rchild); } } /** * 遞迴中序遍歷二叉樹 */ void recInorder(btree *t) { if (t) { recInorder(t->lchild); printf("%c", t->item); recInorder(t->rchild); } } /** * 遞迴後序遍歷二叉樹 */ void recPostorder(btree *t) { if (t) { recPostorder(t->lchild); recPostorder(t->rchild); printf("%c", t->item); } }
非遞迴前序、中序遍歷演算法(c語言實現)
/**
* 非遞迴前序遍歷
*/
void preorderTraverse(btree *t)
{
btree *p = t;
// 初始化棧
stack *s = (stack *)malloc(sizeof(stack));
s->top = 0;
while (p || s->top > 0) {
if (p) {
printf("%c", p->item);
s->db[s->top ++] = p;
p = p->lchild;
} else {
p = s->db[-- s->top];
p = p->rchild;
}
}
}
/**
* 非遞迴中序遍歷
*/
void inorderTraverse(btree *t)
{
btree *p = t;
// 初始化棧
stack *s = (stack *)malloc(sizeof(stack));
s->top = 0;
while (p || s->top > 0) {
if (p) {
s->db[s->top ++] = p;
p = p->lchild;
} else {
p = s->db[-- s->top];
printf("%c", p->item);
p = p->rchild;
}
}
}
非遞迴後序遍歷演算法(c語言實現)
演算法思想:
- 首先,也是找到最左邊的葉子結點並把路上遇到的節點依次入棧
- 然後,彈出棧頂元素(該元素為最左邊的葉子),判斷(1)它是否有右節點(2)如果有右節點,是否被訪問過。如果滿足(1)有右節點並且(2)右節點沒有訪問過,說明這是後序遍歷的相對根節點,因此需要將這個節點再次入棧,並且它的右節點入棧,然後重新執行第一步。否則,就訪問該節點,並且設定pre為此節點,同時把將遍歷節點附空值,訪問進入無限迴圈
演算法程式碼:
/**
* 非遞迴後序遍歷
*/
void postTraverse(btree *t)
{
btree *p, *pre;
p = t;
pre = NULL;
// 初始化棧
stack *s = (stack *)malloc(sizeof(stack));
s->top = 0;
while (p || s->top > 0) {
if (p) {
s->db[s->top ++] = p;
p = p->lchild;
} else {
p = s->db[-- s->top];
if (p->rchild != NULL && p->rchild != pre) { // p為相對根節點
s->db[s->top ++] = p;
p = p->rchild;
} else {
printf("%c", p->item);
pre = p;
p = NULL;
}
}
}
}
注意:
嚴蔚敏的<<資料結構>>上有一段話很經典,摘錄如下:”從二叉樹遍歷的定義可知,三種遍歷演算法之不同處僅在於訪問根節點和遍歷左、右子樹的先後關係。如果在演算法中暫且抹去和遞迴無關的visit語句,則三個遍歷演算法完全相同。因此,從遞迴執行過程的角度來看,前序、中序、後序遍歷也完全相同。“ 這段話給我們的提示就是,前序、中序、後序遍歷的演算法相同,只是printf()語句位置而已。根據前序序列、中序序列構建二叉樹
函式定義
bt rebuildTree(char *pre, char *in, int len);
引數: * pre:前序遍歷結果的字串陣列 * in:中序遍歷結果的字串陣列 len : 樹的長度 例如: 前序遍歷結果: a b c d e f g h 中序遍歷結果: c b e d f a g h
演算法思想
- 遞迴思想,遞迴的終止條件是樹的長度len == 0
- 在中序遍歷的陣列中找到前序陣列的第一個字元,記錄在中序陣列中的位置index.如果找不到,說明前序遍歷陣列和中序遍歷陣列有問題,提示錯誤資訊,退出程式即可;找到index後,新建一個二叉樹節點t,t->item = *pre,然後遞迴的求t的左孩子和有孩子
- 遞迴的左孩子:void rebuildTree(pre + 1, in, index)
- 遞迴的右孩子:void rebuildTree(pre + (index + 1), in + (index + 1), len - (index + 1))
實現程式碼(c語言版)
/**
* Description:根據前序和中序構建二叉樹
*/
bt rebuildTree(char *pre, char *in, int len)
{
bt t;
if(len <= 0)
{
//遞迴終止
t = NULL;
}else
{
//遞迴主體
int index = 0;
while(index < len && *(pre) != *(in + index))
{
index ++;
}
if(index >= len)
{
printf("前序遍歷或者中序遍歷陣列有問題!\n");
exit(-1);
}
t = (struct bintree *)malloc(sizeof(struct bintree));
t->item = *pre;
t->lchild = rebuildTree(pre + 1, in, index);
t->rchild = rebuildTree(pre + (index + 1), in + (index + 1), len - (index + 1));
}
return t;
}
根據中序序列、後序序列構建二叉樹
函式定義
/**
* 中序、後序序列構建二叉樹
*/
btree* rebuildTree(char *order, char *post, int len);
演算法思想
中序序列:C、B、E、D、F、A、H、G、J、I 後序序列:C、E、F、D、B、H、J、I、G、A 遞迴思路:- 根據後序遍歷的特點,知道後序遍歷最後一個節點為根節點,即為A
- 觀察中序遍歷,A左側CBEDF為A左子樹節點,A後側HGJI為A右子樹節點
- 然後遞迴的構建A的左子樹和後子樹
實現程式碼(c程式碼)
/**
* 根據中序和後序序列構建二叉樹
* (ps:昨晚參加阿里筆試,等到最後說可以免筆試直接面試,今天估計還是要根據學校篩選,哈哈,為了這點
* 也得參加阿里筆試,不能讓自己的學校受到鄙視)
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int n;
typedef struct btree {
struct btree *lchild;
struct btree *rchild;
char data;
} btree;
/**
* 中序、後序序列構建二叉樹
*/
btree* rebuildTree(char *order, char *post, int len)
{
btree *t;
if (len <= 0) {
return NULL;
} else {
int index = 0;
while (index < len && *(post + len - 1) != *(order + index)) {
index ++;
}
t = (btree *)malloc(sizeof(btree));
t->data = *(order + index);
t->lchild = rebuildTree(order, post, index);
t->rchild = rebuildTree(order + index + 1, post + index, len - (index + 1));
}
return t;
}
/**
* 前序遍歷二叉樹
*/
void preTraverse(btree *t)
{
if (t) {
printf("%c ", t->data);
preTraverse(t->lchild);
preTraverse(t->rchild);
}
}
int main(void)
{
int i;
char *post, *order;
btree *t;
while (scanf("%d", &n) != EOF) {
post = (char *)malloc(n);
order = (char *)malloc(n);
getchar();
for (i = 0; i < n; i ++)
scanf("%c", order + i);
getchar();
for (i = 0; i < n; i ++)
scanf("%c", post + i);
t = rebuildTree(order, post, n);
preTraverse(t);
printf("\n");
free(post);
free(order);
}
return 0;
}
遞迴清理二叉樹
/**
* 清理二叉樹
*/
void cleanBtree(btree *t)
{
if (t) {
cleanBtree(t->lchild);
cleanBtree(t->rchild);
free(t);
}
}