二叉樹的前中後序非遞歸遍歷實現

二叉樹的遞歸遍歷實現非常簡單,而根據根節點的訪問順序分爲前中後三種遍歷順序,下面是遞歸遍歷實現的代碼:

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

typedef struct treeNode {
	int data;
	struct treeNode *left;
	struct treeNode *right;
} treeNode;

void printTreeRecursiveFirst(treeNode *root)
{
        // 先序遍歷
	printf("%d\n", root->data);
	if (root->left) {
		printTreeRecursive(root->left);
	}
	if (root->right) {
		printTreeRecursive(root->right);
	}
}

void printTreeRecursiveMiddle(treeNode *root)
{
	if (root->left) {
		printTreeRecursive(root->left);
	}
        // 中序遍歷
	printf("%d\n", root->data);
	if (root->right) {
		printTreeRecursive(root->right);
	}
}

void printTreeRecursiveLast(treeNode *root)
{
	if (root->left) {
		printTreeRecursive(root->left);
	}
	if (root->right) {
		printTreeRecursive(root->right);
	}
        // 後序遍歷
	printf("%d\n", root->data);
}

那有沒有辦法不用遞歸來實現二叉樹的遍歷呢,實際上函數調用的實現主要藉助是棧這種後進先出的數據結構,我們自然會想到使用棧結構來實現二叉樹的非遞歸遍歷。

 

實際上,對於如下的一棵二叉樹,利用棧來遍歷,思路主要如下:

第一步我們從根節點開始,不停的遍歷左節點,並把每次遍歷到的左節點壓入棧中,直到沒有左節點結束,對應上圖1、2、4、6四個節點被壓入棧中。

第二步,依次彈出第一步中壓入的節點,每次彈出一個節點,訪問它的右節點,此時對應上圖中的節點6被彈出,它的右節點是9。

第三步,每次從棧中彈出一個節點,訪問該節點的右節點,並把這個右節點看成一棵新的樹,再次應用第一步和第二步的操作。

第四步,重複執行第三步,直到棧中的節點全部彈出,此時二叉樹的所有節點都被遍歷到了。

 

對於這種利用棧的遍歷方式,不同的打印節點值的時機也就決定了我們是用什麼順序在遍歷。

先序遍歷:先打印節點的值,然後再壓入棧中

中序遍歷:節點彈出棧的時候打印節點的值

後序遍歷:節點彈出棧,此時需要等到把它的右節點也處理完才能打印,所以需要把這個節點再壓回去,等到該節點的右節點都處理完第二次彈出棧的時候打印。

 

相應的代碼如下:

typedef struct linkNode {
	treeNode *tNode;
	struct linkNode *next;
} linkNode;

typedef struct stack {
	linkNode head;
} stack;

stack *stack_init()
{
	stack *s = malloc(sizeof(stack));
	s->head.next = NULL;
	return s;
}

void stack_push(stack *s, treeNode *tNode)
{
	linkNode *lNode = malloc(sizeof(linkNode));
	lNode->tNode = tNode;
	if (s->head.next) {
		lNode->next = s->head.next;
		s->head.next = lNode;
	} else {
		s->head.next = lNode;
		lNode->next = NULL;
	}
}

treeNode *stack_pop(stack *s) {
	if (s->head.next == NULL) {
		return NULL;
	}
	linkNode *tmp = s->head.next;
	s->head.next = tmp->next;
	treeNode *tNode = tmp->tNode;
	free(tmp);
	return tNode;
}

int stack_is_empty(stack *s)
{
	return s->head.next == NULL;
}



// 先序遍歷
void printTreeFirst(treeNode *root)
{
	treeNode *tmp = root;
	stack *s = stack_init();
	
	while(tmp) {
		printf("%d\n", tmp->data);
		stack_push(s, tmp);
		tmp = tmp->left;
	}
	
	while(!stack_is_empty(s)) {
		tmp = stack_pop(s);
		tmp = tmp->right;
		while (tmp) {
			printf("%d\n", tmp->data);
			stack_push(s, tmp);
			tmp = tmp->left;
		}
	}
}

// 中序遍歷
void printTreeMiddle(treeNode *root)
{
	treeNode *tmp = root;
	stack *s = stack_init();
	
	while(tmp) {
		stack_push(s, tmp);
		tmp = tmp->left;
	}
	
	while(!stack_is_empty(s)) {
		tmp = stack_pop(s);
		printf("%d\n", tmp->data);
		tmp = tmp->right;
		while (tmp) {
			stack_push(s, tmp);
			tmp = tmp->left;
		}
	}
}




後序遍歷有兩種實現思路,第一種就如上所說,節點第二次彈出棧的時候打印節點的值,那如何知道節點是否是第二次彈出呢,我們可以給節點加個標誌位,標誌位的初始值爲0,在第二次壓棧前把標誌位的值置爲1,這樣每次在彈出節點的時候判斷一下這個標誌位的值,如果是1就打印節點的值。

typedef struct treeNode {
	int data;
	int isSecond;  // 增加標誌位,初始值賦爲0
	struct treeNode *left;
	struct treeNode *right;
} treeNode;


// 後序遍歷1
void printTreeLast(treeNode *root)
{
	treeNode *tmp = root;
	stack *s = stack_init();
	
	while(tmp) {
		stack_push(s, tmp);
		tmp = tmp->left;
	}
	
	while(!stack_is_empty(s)) {
		tmp = stack_pop(s);
		if(tmp->isSecond) {
			printf("%d\n", tmp->data);
		} else {
			tmp->isSecond = 1;
			stack_push(s, tmp);	
			tmp = tmp->right;
			while (tmp) {
				stack_push(s, tmp);
				tmp = tmp->left;
			}
		}
	}
}

另一種思路:

我們知道先序遍歷是:根-->左-->右,而後序遍歷是:左-->右-->根,其實我們只要把先序遍歷稍微修改變成:根-->右-->左,就發現它正好是後序遍歷的倒序,那麼我們只要根據 根-->右-->左遍歷,把打印節點變成壓入一個臨時的棧中,結束後再對這個臨時的棧依次彈棧打印節點,最終就是我們要的後序遍歷。

// 後序遍歷2
void printTreeLast(treeNode *root)
{
	treeNode *tmp = root;
	stack *s = stack_init();
	stack *s2 = stack_init();
	
	while(tmp) {
		stack_push(s2, tmp); // 打印改爲壓棧,臨時保存起來
		stack_push(s, tmp);
		tmp = tmp->right;
	}
	
	while(!stack_is_empty(s)) {
		tmp = stack_pop(s);
		tmp = tmp->left;
		while (tmp) {
			stack_push(s2, tmp);
			stack_push(s, tmp);
			tmp = tmp->right;
		}
	}
	
	// 再依次打印棧中的節點
	while(!stack_is_empty(s2)) {
		treeNode *tmp = stack_pop(s2);
		printf("%d\n", tmp->data);
	}
}

 

That’s all!

 

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