从递归(代码)的角度理解二叉树的先序/中序/后序三种遍历顺序

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zhang197093/article/details/80138434

二叉树的遍历原理其实是很简单的,就是从二叉树的根节点开始,把它的左节点以及左节点下面的节点再当作一棵二叉树,和右节点以及右节点下面的节点也再当作一棵二叉树,依此类推,直到节点下面没有节点为止。

二叉树的有三种遍历顺序:先序遍历、中序遍历、后序遍历,而这个顺序都是以根节点作为参照的,最先遍历根节点就是先序遍历,最后遍历根节点就是后序遍历,根节点放在左右节点之间的情况就是中序遍历。

先看代码:

#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!

猜你喜欢

转载自blog.csdn.net/zhang197093/article/details/80138434