從遞歸(代碼)的角度理解二叉樹的先序/中序/後序三種遍歷順序

二叉樹的遍歷原理其實是很簡單的,就是從二叉樹的根節點開始,把它的左節點以及左節點下面的節點再當作一棵二叉樹,和右節點以及右節點下面的節點也再當作一棵二叉樹,依此類推,直到節點下面沒有節點爲止。

二叉樹的有三種遍歷順序:先序遍歷、中序遍歷、後序遍歷,而這個順序都是以根節點作爲參照的,最先遍歷根節點就是先序遍歷,最後遍歷根節點就是後序遍歷,根節點放在左右節點之間的情況就是中序遍歷。

先看代碼:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct treenode {
	void *data;
	struct treenode *left;
	struct treenode *right;
} treenode;

typedef struct tree {
	treenode *root;  //根節點
	//其他屬性...
} tree;

treenode *make_node(void *data){
	treenode *node = malloc(sizeof(treenode));
	bzero(node, sizeof(*node));
	node->data = data;
	node->left = NULL;
	node->right = NULL;
	return node;
}

// 先序遍歷 根節點 -> 左節點 -> 右節點
void first_sort_var_dump(const treenode *node) {
	if (node == NULL) {
		return;
	}
	printf("node->data: %s\n", (const char *)node->data);

	first_sort_var_dump(node->left);
	first_sort_var_dump(node->right);
}

// 中序遍歷 左節點 -> 根節點 -> 右節點
void middle_sort_var_dump(const treenode *node) {
	if (node == NULL) {
		return;
	}
	middle_sort_var_dump(node->left);

	printf("node->data:%s\n", (const char *)node->data);

	middle_sort_var_dump(node->right);
}

// 後序遍歷 左節點 -> 右節點 -> 根節點
void last_sort_var_dump(const treenode *node) {
	if (node == NULL) {
		return;
	}
	last_sort_var_dump(node->left);
	last_sort_var_dump(node->right);

	printf("node->data:%s\n", (const char *)node->data);
}

int main(){

	tree *t = malloc(sizeof(tree));
	bzero(t, sizeof(*t));

	treenode *root = make_node("I am root...");
	treenode *node1 = make_node("I am node1...");
	treenode *node2 = make_node("I am node2...");
	treenode *node3 = make_node("I am node3...");
	treenode *node4 = make_node("I am node4...");
	treenode *node5 = make_node("I am node5...");
	treenode *node6 = make_node("I am node6...");
	treenode *node7 = make_node("I am node7...");
	treenode *node8 = make_node("I am node8...");
	treenode *node9 = make_node("I am node9...");
	treenode *node10 = make_node("I am node10...");
	treenode *node11 = make_node("I am node11...");

	t->root = root;

	root->left = node1;
	root->right = node2;

	node1->left = node3;
	node1->right = node4;

	node3->left = node5;
	node3->right = node6;

	node5->left = node8;
	node8->right = node9;

	node2->left = node10;
	node2->right = node7;

	node7->left = node11;

	printf("先序遍歷....\n");
	first_sort_var_dump(t->root);

	printf("\n中序遍歷...\n");
	middle_sort_var_dump(t->root);

	printf("\n後序遍歷...\n");
	last_sort_var_dump(t->root);

	return 0;
}

以上代碼構造了一顆如下關係所示的二叉樹:


從代碼中可以非常直觀的看到三種遍歷順序之間的不同,先序遍歷是先獲取根節點的值 然後對左右節點進行遞歸,中序遍歷是先遞歸左節點,然後獲取根節點的值,最後遞歸右節點,後序遍歷原理相同,然後結合遞歸的壓棧和彈棧順序,就可以分析出遍歷的順序了,所以說三種遍歷順序就是不同的遞歸調用順序的表現

所以表現出以下的打印結果:

先序遍歷....
node->data: I am root...
node->data: I am node1...
node->data: I am node3...
node->data: I am node5...
node->data: I am node8...
node->data: I am node9...
node->data: I am node6...
node->data: I am node4...
node->data: I am node2...
node->data: I am node10...
node->data: I am node7...
node->data: I am node11...

中序遍歷...
node->data:I am node8...
node->data:I am node9...
node->data:I am node5...
node->data:I am node3...
node->data:I am node6...
node->data:I am node1...
node->data:I am node4...
node->data:I am root...
node->data:I am node10...
node->data:I am node2...
node->data:I am node11...
node->data:I am node7...

後序遍歷...
node->data:I am node9...
node->data:I am node8...
node->data:I am node5...
node->data:I am node6...
node->data:I am node3...
node->data:I am node4...
node->data:I am node1...
node->data:I am node10...
node->data:I am node11...
node->data:I am node7...
node->data:I am node2...
node->data:I am root...

先序比較相對好理解,這裏以後序爲例介紹一下遞歸調用棧的順序

1、對 node1 和 node2 的遞歸分別稱作 func1 和 func2,func1 執行完再執行func2,最後打印root節點的值,棧示意圖如下:


2、此時程序執行func1,func1 又對node3 和 node4 進行遞歸,把遞歸函數稱爲 func3 和 func 4,先調用 func3, func3 進棧


3、依次類推,node5的遞歸func5 和 node8的遞歸func8 依次進棧


4、func8 中 又對node8的左右節點進行遞歸,由於node8沒有左節點 該遞歸我們記爲 func8left,此時執行func8left, 也就是func8left進棧,由於func8left的參數爲null,此時函數沒有後續遞歸調用直接返回,然後彈棧了

 

5、func8left執行完並彈棧,此時對node9的遞歸 func9 進棧


6、node9的左右子節點都爲null,所有 func9left 進棧啥也不做然後彈棧,接着 func9right 進棧 也啥都不做彈棧,然後打印node9的值, func9執行結束彈棧

7、func8 打印 node8的值彈棧

8、func5 打印node5的值彈棧

9、由於先打印左右子節點,func5 代碼之後執行 node6 的遞歸 func6,func6 進棧

10、func6的左右節點都爲null,打印 node6的值, func6 彈棧, func3 打印node3的值彈棧

11、然後打印node4和node1的值,func1彈棧之後 ,執行二叉樹右半部分node2的遞歸 func2

12、右半部分執行完最後打印root的值

從以上看出,從遞歸的角度分析二叉樹的三種不同遞歸順序相對純理論的方式更形象,也更容易理解。

that's all!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章