實戰資料結構(11)_二叉樹的遍歷

YunShell發表於2014-03-24
/**********************************************************************
二叉樹的基本操作
(1)二叉樹的資料結構
(2)二叉樹的構造
(3)二叉樹遍歷 :先序,中序,後序
************************************************************************/
#include <cstdio>
#include <cstdlib>
const int MAX=20;
//二叉樹的結構 利用雙連結串列實現
typedef struct Node
{
	char ch;				 // 資料域
	struct Node *plchild;    //左子樹指標
	struct Node *prchild;	//右子樹指標
}BiTreeNode;

//構造二叉樹 
//1.按照先序遍歷法構造 利用遞迴構造
// ABD#G##EH##I##CF#J###
void CreateBiTree_PreOrder(BiTreeNode * &T) //注意這裡傳入的是指標的引用
{	
	char ch;
	scanf("%c",&ch);
	if('#'==ch)
		T=NULL;
	else {
		T=new BiTreeNode;
		if(NULL==T)
			exit(-1);
		T->ch=ch;
		CreateBiTree_PreOrder(T->plchild); // 構造左子樹
		CreateBiTree_PreOrder(T->prchild); // 構造右子樹
	}
}
//2.按照帶括號的字串建立二叉樹結構
/*思路:a(b(c,d),e(f(,g),h(i)))
	'(': 表明已建立的節點為雙親節點,入棧。將要建立左孩子節點,用flag=1標記
	',': 表明將要建立右孩子節點。用flag=2標記
	')': 表明左右孩子節點建立和連結完畢,父節點出棧。
	default: 建立節點,並且賦值,判斷連結情況
 */
void CreateBiTree_ByString(BiTreeNode* &T, char str[])
{
	BiTreeNode* Stack[MAX];		// 棧用於儲存父節點
	int top=0,flag=0;
	BiTreeNode* p=NULL;			//用於臨時指標
	while(*str)
	{
		switch(*str)
		{
			case '(':
					Stack[top++]=p;
					flag=1;		//表示即將要建立的是 左孩子節點
					break;
			case ',':
					flag=2;  //表明將要建立	右孩子節點
					break;
			case ')':
					--top;	//將父節點出棧
					break;
			default:
				{	
					p=new BiTreeNode;
					if(NULL==p)
						return  ;
					if(T==NULL)
						T=p;
					p->ch=*str;
					p->plchild=NULL;
					p->prchild=NULL;
					switch(flag) //此時 flag要初始化 不然非法訪問
					{
						case 1:
							Stack[top-1]->plchild=p; //將父節點與左孩子節點連結
							break;
						case 2:
							Stack[top-1]->prchild=p; //將父節點與右孩子節點連結
							break;
					}
					break;
				}
		}
		++str;
	}
}
// 遞迴先序遍歷
void PreOrderTraverse_Recur(BiTreeNode *T)
{	
	if(T) 
	{
		printf("%2c",T->ch);
		PreOrderTraverse_Recur(T->plchild);
		PreOrderTraverse_Recur(T->prchild);
	}
}
// 非遞迴先序遍歷
/* 思路: 先訪問父節點,列印。然後壓入棧中,然後訪問左孩子節點,訪問列印壓棧。
		將左孩子一個個壓入棧中,直到NULL.然後在訪問右孩子節點。同理按前
 */
void PreOrderTraverse_NoRecur(BiTreeNode *T)
{
	BiTreeNode* Stack[MAX]; //利用陣列構造棧 先進後出
	int top=0;
	BiTreeNode *p=T;
	while( p || top >0)
	{
		while(p)
		{
			printf("%2c",p->ch);	//先訪問
			Stack[top++]=p;		//將 父節點壓棧
			p=p->plchild;		//訪問左孩子節點
		}
		if(top>0)	//top 代表棧中有多個節點
		{ //開始訪問 右孩子節點
			p=Stack[--top];		//如果某個父節點的左孩子節點為NULL 那麼出棧該父節點。
			p=p->prchild;		//然後又跳入到 訪問該右孩子的左子樹
		}
	}
}
// 遞迴中序遍歷
void InOrderTraverse_Recur(BiTreeNode *T)
{
	if(T)  //如果節點不為NULL 
	{	
		InOrderTraverse_Recur(T->plchild); //先放問 左孩子樹
		printf("%2c",T->ch);		   //然後在 訪問根節點
		InOrderTraverse_Recur(T->prchild); // 然後在訪問 右孩子樹
	}
}
// 非遞迴中序遍歷 
/* 思路:相比於先序而言(一直將父節點壓棧,父節點出棧的時候就是要訪問該節點的右孩子樹)
		 所以中序遍歷,一直壓入左孩子節點,直到NULL.在出棧的時候列印父節點值。
 */
void InorderTraverse_NoRecur(BiTreeNode *T)
{
	BiTreeNode* Stack[MAX];
	int top=0;
	BiTreeNode *p=T;
	while( p || top>0 )
	{
		while(p)
		{
			Stack[top++]=p;  //直接將父節點入棧
			p=p->plchild;	// 然後訪問左子樹
		}
		if(top>0)
		{
			p=Stack[--top];		 //將父節點處棧
			printf("%2c",p->ch); //列印父節點值
			p=p->prchild;		 //訪問右孩子子樹
		}
	}
}
// 遞迴後序遍歷
void AfterOrderTraverse_Recur(BiTreeNode *T)
{
	if(T)
	{
		AfterOrderTraverse_Recur(T->plchild);
		AfterOrderTraverse_Recur(T->prchild);
		printf("%2c",T->ch);	
	}
}
// 非遞迴後序遍歷
/*思路:依舊是將父節點依次壓棧。直到左節點為NULL,此時不出棧父節點,而是直接訪問右孩子節點
		同時設定q用於檢測是否已經訪問過右孩子節點。
 */
void AfterOrderTraverse_NoRecur(BiTreeNode *T)
{
	BiTreeNode* Stack[MAX];
	int top=0;
	BiTreeNode *p=T,*q=NULL; // q用於判斷右節點是否訪問過
	while(p || top>0)
	{
		while(p) //直到沒有左孩子節點
		{
			Stack[top++]=p;
			p=p->plchild; 
		}
		if(top>0)
		{
			p=Stack[top-1]; //不出棧 直接訪問右子樹
			if(p->prchild==NULL || p->prchild == q) //沒有右節點或者右節點已經訪問過
			{
				printf("%2c",p->ch);
				q=p;
				p=NULL;
				top--;
			}
			else //有右節點 且沒被訪問過
				p=p->prchild;
		}
	}
	
}
//銷燬二叉樹
void DestoryBiTree(BiTreeNode* &T)
{
	if(T)
	{
		if(T->plchild)
			DestoryBiTree(T->plchild);
		if(T->prchild)
			DestoryBiTree(T->prchild);
		delete T;
		T=NULL;
	}
}
int main()
{//ABD#G##EH##I##CF#J###
	BiTreeNode *T=NULL;
	CreateBiTree_PreOrder(T);
	puts("遞迴先序遍歷:");
	PreOrderTraverse_Recur(T);
	puts("");
	puts("非遞迴先序遍歷:");
	PreOrderTraverse_NoRecur(T);
	puts("");
	puts("遞迴中序遍歷:");
	InOrderTraverse_Recur(T);
	puts("");
	puts("非遞迴中序遍歷:");
	InorderTraverse_NoRecur(T);
	puts("");
	puts("遞迴後序遍歷:");
	AfterOrderTraverse_Recur(T);
	puts("");
	puts("非遞迴後序遍歷:");
	AfterOrderTraverse_NoRecur(T);
	puts("");
	DestoryBiTree(T);	//銷燬二叉樹
	puts("\n--按照帶括號的字串輸入------------");
	BiTreeNode *T2=NULL;
	char str[]="a(b(c,d),e(f(,g),h(i)))";
	CreateBiTree_ByString(T2,str);
	puts("遞迴先序遍歷:");
	PreOrderTraverse_Recur(T2);
	puts("");
	puts("非遞迴先序遍歷:");
	PreOrderTraverse_NoRecur(T2);
	puts("");
	puts("遞迴中序遍歷:");
	InOrderTraverse_Recur(T2);
	puts("");
	puts("非遞迴中序遍歷:");
	InorderTraverse_NoRecur(T2);
	puts("");
	puts("遞迴後序遍歷:");
	AfterOrderTraverse_Recur(T2);
	puts("");
	puts("非遞迴後序遍歷:");
	AfterOrderTraverse_NoRecur(T2);
	puts("");
	//DestoryBiTree(T2);	//銷燬二叉樹
}

相關文章