鏈式二叉樹的7種遍歷方式

在說鏈式二叉樹的遍歷之前,我們先來說一下如何創建一個鏈式二叉樹。創建一個鏈式二叉樹,需要一個已知的字符串,這個字符串必須支持創建二叉樹的規則。比如:
我們通過遞歸來實現二叉樹的創建,開始先向左走,如果遇到#,就返回,然後向右走,直到遇到#,就返回一個NULL,這樣,就完成了一個鏈式二叉樹的創建。

這裏先將除了主要函數外的其他調用到的函數放在這裏,供查閱

頭文件

#ifndef _BTREE_H__
#define _BTREE_H__

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

typedef char BTDataType;

typedef struct BinaryTreeNode 
{
	BTDataType data;
	struct BinaryTreeNode* lchild;
	struct BinaryTreeNode* rchild;
}BTNode;

typedef BTNode* QUDataType;

typedef BTNode* STDataType;


typedef struct Stack
{
	STDataType* _a;
	int _top;
	int _capacity;

}Stack;

typedef struct QueueNode 
{ 
	struct QueueNode* _next;
	QUDataType _data;
}QueueNode;

typedef struct Queue
{
	QueueNode *head;
	QueueNode *rear;
}Queue;

BTNode* BinaryTreeCreate(BTDataType* src);

void BinaryTreePrevOrder(BTNode* root); 
void BinaryTreeInOrder(BTNode* root); 
void BinaryTreePostOrder(BTNode* root);

void BinaryTreeLevelOrder(BTNode* root);
void BinaryTreePrevOrderNonR(BTNode* root);
void BinaryTreeInOrderNonR(BTNode* root);
void BinaryTreePostOrderNonR(BTNode* root);

void BIaryTreeLevelPrint(BTNode* root, int num);

#endif//_BTREE_H__
BTNode* BinaryTreeCreate(BTDataType* src)//鏈式二叉樹的創建
{
	static int n = 0;
	if (src[n] == '#')
	{
		n++;
		return NULL;
	}
	BTNode *cur = (BTNode*)malloc(sizeof(BTDataType));
	cur->data = src[n];
	n++;

	cur->lchild = BinaryTreeCreate(src);
	cur->rchild = BinaryTreeCreate(src);

	return cur;
}

void QueueInit(Queue* qu)//隊列的初始化
{
	qu->head = qu->rear = (QueueNode *)malloc(sizeof(QueueNode));
}
void QueuePop(Queue* qu)//出對頭
{
	qu->head = qu->head->_next;
}
void QueueDestory(Queue* qu)//銷燬隊列
{
	free(qu->head);

}
void QueuePush(Queue* qu, QUDataType x)//隊列中進入元素
{
	qu->rear->_data = x;
	qu->rear->_next = (QueueNode *)malloc(sizeof(QueueNode));
	qu->rear = qu->rear->_next;   
	qu->rear->_data = NULL;
}

STDataType StackTop(Stack * st)//拿取棧頂
{
	return st->_a[st->_top - 1];
}
void StackInit(Stack *st)//棧的初始化
{
	st->_a = (STDataType)malloc(sizeof(STDataType)* 10);
	st->_capacity = 10;
	st->_top = 0;

}
void StackPop(Stack *st)//退棧
{
	st->_top--;
}
void StackPush(Stack *st,STDataType  x)//壓棧
{
	if (st->_top == st->_capacity - 1)
	{
		realloc(st->_a, sizeof(STDataType)* 2 * st->_capacity);
	}
	st->_a[st->_top] = x;
	st->_top++;

}
void StackDestory(Stack *st)//銷燬棧
{
	free(st->_a);
}

二叉樹的鏈式結構的7種遍歷方法

遞歸遍歷:

  1. 前序遍歷  

前序遍歷的思路是,通過遞歸的方式,讓鏈式二叉樹,先不停地從左孩子向下,每遇到一個節點,就進行該節點打印,直到遇到“#”,然後又從右孩子開始向下走,直到遇到“#”,就返回上一個節點,然後又從這個節點開使向右孩子的位置向下。

void BinaryTreePrevOrder(BTNode* root)//遞歸式的前序遍歷
{
	printf("%c ", root->data);
	if (root->lchild)
	{
		BinaryTreePrevOrder(root->lchild);
	}
	if (root->rchild)
	{
		BinaryTreePrevOrder(root->rchild);
	}

}
  1. 中序遍歷

中序遍歷和前序遍歷一樣,唯一的差別是,先進型向左孩子的查找,然後進行一個,遇到“#”後,就進行該節點的打印,然後再進行右孩子的查找。

void BinaryTreeInOrder(BTNode* root)//遞歸式的中序遍歷
{
	
	if (root->lchild)
	{
		BinaryTreeInOrder(root->lchild);
	}
	printf("%c ", root->data);
	if (root->rchild)
	{
		BinaryTreeInOrder(root->rchild);
	}
}

 

  1. 後續遍歷

和中序,前序一樣,也僅僅只是打印的順序發生了改變。

void BinaryTreePostOrder(BTNode* root)//遞歸式的後序遍歷
{
	
	if (root->lchild)
	{
		BinaryTreePostOrder(root->lchild);
	}
	if (root->rchild)
	{
		BinaryTreePostOrder(root->rchild);
	}
	printf("%c ", root->data);
}

       層序遍歷:

       層序遍歷是通過一個隊列來實現的,通過利用隊列先入後出的原則,來完成層序遍歷。思路如下:從根節點開始,先將根節點打印,然後如果有左孩子,就將左孩子入隊列,如果有右孩子,就將右孩子入隊列。然後從隊列中出一個元素,將這個元素打印,然後如果有左孩子,就將左孩子入隊列,如果有右孩子,就將右孩子入隊列,就這樣,如果有一個孩子是NULL;那就看另外一個孩子,如果都是NULL,那就只進行一個出隊列的打印就好了,這樣,層層循環,就完成了層序遍歷。

void BinaryTreeLevelOrder(BTNode* root)//層序遍歷
{
	Queue qu;
	QueueInit(&qu);
	while (root)
	{
		putchar(root->data);

		if (root->lchild)
		{
			QueuePush(&qu, root->lchild);//入隊
		}
		if (root->rchild)
		{
			QueuePush(&qu,root->rchild);//入隊
		}
		root = qu.head->_data;//拿取對頭

		QueuePop(&qu);//出對頭
	}

	//QueueDestory(&qu);

}

       非遞歸的遍歷:

 

  1. 前序遍歷

用非遞歸方式的前序遍歷,是利用自己模擬一個棧,通過棧的先入後出的原則,進行一個前序遍歷。思路如下:我們在一開始,先將根節點打印,然後將根節點的右孩子進棧(右孩子不爲NULL),將根節點變爲左孩子(左孩子不爲NULL),然後繼續執行的讓根節點的右孩子進棧,打印根節點,直到遇到NULL,這個時候,我們已經將左孩子遍歷了一遍,需要進入右孩子了,我們進行一個拿取棧頂的操作,然後讓節點變爲棧頂的那個元素,同時進行一次退棧,這個時候,拿到地棧頂就是這個葉子節點的根節點的右孩子了,這樣進行一個循環,就完成了非遞歸的前序遍歷。

void BinaryTreePrevOrderNonR(BTNode* root)//非遞歸式的前序遍歷
{
	Stack st;
	StackInit(&st);
	while (root)
	{
		putchar(root->data);//先進行一個根節點的打印
		if (root->rchild)
		{
			StackPush(&st,root->rchild);//右孩子進棧
		}
		if (root->lchild)
		{
			root = root->lchild;//左孩子進棧
		}
		else
		{
			if (st._top == 0)//判斷在退棧之前 棧是否是空
			{
				break;
			}
			root = st._a[st._top - 1];
			StackPop(&st);
		}
	}

}

 

  1. 中序遍歷

思路:中序遍歷,是先進行一個根節和根節點所有左孩子的進棧,然後在遇到根節點的左孩子是NULL時,然後根節點變爲棧頂的元素,進行一個根節點的打印,進行一次退棧操作,然後去根節點的右孩子的位置,繼續進行一個根節點的左孩子進棧操作。就這樣,利用一個循環,完成這樣的非遞歸式的中序遍歷。

void BinaryTreeInOrderNonR(BTNode* root)//非遞歸式的中序遍歷
{
	BTNode *cur = root;
	Stack st;
	StackInit(&st);

	while (1)
	{
		for (;cur;cur = cur->lchild)//所有根節點的左孩子進棧
		{
			StackPush(&st,cur);
		}
		cur = StackTop(&st);//拿取棧頂

		putchar(cur->data);
		StackPop(&st);//退棧

		cur = cur->rchild;//進入右孩子

		if (!cur)
		{
			if (st._top == 0)//判斷在退棧之前 棧是否是空
			{
				break;
			}
		}
	}
	StackDestory(&st);
}
  1. 後序遍歷

後續遍歷的要求是,在左孩子和右孩子都已經進行了打印後,纔打印父親節點,這樣的話,我們就要直到從左孩子回來時,父親節點被訪問的次數,因爲父親節點聯繫着左右兩個孩子節點,二叉樹通過左節點回來的根節點,需要經過一個自己的父親節點,然後才能去到右孩子的位置,因此,我們需要一個標記,來標出,父親節點被訪問的次數。如果父親節點沒有被左孩子返回時訪問,這個標記就是0,如果被左孩子返回訪問了一次,就記做1。這時,我們可以定義一個數組,這個數組的下表等於棧空間中父親節點的位置。我們通過進行最初始的根節點的入棧開始,一直向左孩子方向進行入棧操作,直到遇到NULL停止,這個時候,棧頂的下標就代表了自己在數組中的訪問次數,我們進行一個拿取棧頂的操作,同時將數組中這個節點的位置,進行一個置1的操作,這樣,如果下一次拿去棧頂的時候,如果發現這個棧頂在數組中的數是1的時候,就代表這個棧頂,已經被訪問過一次,就進行一個棧頂的打印,這裏的打印是一個循環打印,如果根節點及他的父親節點的標記都是1的話,就依次向上進行打印,直到遇到0的位置停止,每次打印完了就進行一次退棧。最後通過一個循環,就完成了一個非遞歸式的後序遍歷。

void BinaryTreePostOrderNonR(BTNode* root)//非遞歸式的後序遍歷
{
	BTNode *cur = root;
	Stack st;
	int arr[100] = { 0 };//創建一個數組
	StackInit(&st);
	while (1)
	{
		for (;cur;cur = cur->lchild)//所有根節點的左孩子進棧
		{
			StackPush(&st, cur);
			arr[st._top - 1] = 0;
		}

		while (arr[st._top - 1])//關鍵所在,判斷父親節點是否是的數組元素是否是1,若是,則進行一個循環打印,直到遇到是0的停止
		{
			if (st._top == 0)//判斷在退棧之前 棧是否是空
			{
				break;
			}
			cur = StackTop(&st);//拿取棧頂
			putchar(cur->data);
			StackPop(&st);//退棧
		}
		if (st._top == 0)//判斷在退棧之前 棧是否是空
		{
			break;
		}
		cur = StackTop(&st);//拿取棧頂
		arr[st._top - 1] = 1;//將該父親節點的數組元素置1
		cur = cur->rchild;//進入右孩子
	}
	StackDestory(&st);
}

 

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