二叉樹的非遞歸遍歷及層次遍歷

二叉樹的基本操作請看:http://blog.csdn.net/sysu_arui/article/details/7865876

前面簡答介紹了二叉樹的基本操作,包括二叉樹的建立,銷燬,遞歸遍歷,以及其他一些常見的遞歸算法,現在集中討論一下二叉樹的層次遍歷和非遞歸遍歷。

二叉樹的層次遍歷要用到隊列,前序、中序和後序非遞歸遍歷要用到棧,這裏我就不自己寫隊列和棧了,直接使用C++標準庫中的容器適配器queue和stack。

1、 二叉樹的層次遍歷

二叉樹的層次遍歷類似圖的廣度優先遍歷,都用到了隊列。

基本思路:

(1)根結點非空,則入隊列

(2)隊列非空,隊首元素出隊列,輸出結點值,若結點有左孩子,左孩子入隊列;若結點有右孩子,右孩子也入隊列。

(3)重複步驟(2)直到隊列爲空

顯然通過以上步驟,得到的是二叉樹從上至下,從左到右的層次遍歷序列。實現代碼如下:

//二叉樹的層次遍歷,用到了隊列 
void levelOrderTraverse(const BiTree& T)
{
	queue<BiTree> q;
	BiTree p = NULL;
	
	if(T)//若根結點非空,則入隊列 
	{
		q.push(T);
	}
	while(!q.empty())//隊列非空 
	{
		p = q.front();
		q.pop();
		cout<<p->data<<" ";
		if(p->lchild)//左孩子不空,入隊列 
		{
			q.push(p->lchild);
		}
		if(p->rchild)//右孩子不空,入隊列 
		{
			q.push(p->rchild);
		}
	} 
}

2、二叉樹的先序非遞歸遍歷

二叉樹的先序非遞歸遍歷比較簡單,基本思路是先輸出結點值,再入棧,然後遍歷左子樹。退棧時,遍歷棧頂結點的右子樹,看代碼:

//二叉樹的先序非遞歸遍歷 
void iterativePreOrderTraverse(const BiTree& T)
{
	stack<BiTree> s;
	BiTree p = T;
	
	while(p || !s.empty())//p非空,或棧非空
	{
		if(p)
		{
			//輸出根結點,根結點入棧,遍歷左子樹 
			cout<<p->data<<" ";
			s.push(p);
			p = p->lchild;
		}
		else
		{
			//根結點退棧,遍歷右子樹 
			p = s.top();
			s.pop();
			p = p->rchild;//轉右孩子結點 
		}
	}
}

3、二叉樹的中序非遞歸遍歷

二叉樹的中序非遞歸遍歷也很簡單,和先序不同的是,中序是先進棧,再遍歷左子樹;出棧時,才輸出結點值,然後遍歷右子樹,代碼如下:

//二叉樹的中序非遞歸遍歷 
void iterativeInOrderTraverse(const BiTree& T)
{
	stack<BiTree> s;
	BiTree p = T;
	
	while(p || !s.empty())//p非空,或棧非空
	{
		if(p)
		{
			//根指針進棧 ,遍歷左子樹 
			s.push(p);	
			p = p->lchild;
		}
		else
		{
			//根指針退棧,訪問根結點,遍歷右子樹 
			p = s.top();
			s.pop();
			cout<<p->data<<" ";
			p = p->rchild;
		}
	}
}

4.二叉樹的後序非遞歸遍歷

二叉樹的後序非遞歸遍歷,比先序和中序非遞歸遍歷,稍微複雜點。因爲後序遍歷的順序是左子樹->右子樹->根結點,所以我們在遍歷過程中得標記左右子樹的狀態。這裏我們用一個bool變量標識結點p的右子樹遍歷狀態,false表示右子樹未遍歷,true則表示右子樹已遍歷。代碼如下:

(1)版本1

//二叉樹的後序非遞歸遍歷 
void iterativePostOrderTraverse(const BiTree& T)
{
	stack<pair<BiTree,bool> > s;
	BiTree p = T;
	
	while(p || !s.empty())
	{
		if(p)
		{
			s.push(make_pair(p,false));//false表示根結點p的右子樹未訪問 
			p = p->lchild;
		}
		else
		{
			if(s.top().second == false)//根結點的右子樹未訪問 
			{
				s.top().second = true;//標誌右子樹爲已訪問 
				p = s.top().first->rchild;//遍歷右子樹 
			}
			else//右子樹已訪問 
			{
				cout<<s.top().first->data<<" "; //輸出根結點值 
				s.pop();//根結點出棧 
			}
		}	
	}
}
(2)版本2
//後序非遞歸遍歷的另一個版本 
void anotherIterativePostOrderTraverse(const BiTree& T)
{
	stack<pair<BiTree,bool> > s;
	BiTree p = T;
	
	do
	{
		while(p)
		{
			s.push(make_pair(p,false));
			p = p->lchild;
		}
		
		while(!s.empty() && s.top().second == true)
		{
			cout<<s.top().first->data<<" ";
			s.pop();
		}
		
		if(!s.empty())
		{
			s.top().second = true;
			p = s.top().first->rchild;
		}
	}while(!s.empty());
}

注:我們來分析一下,後序非遞歸遍歷的執行過程。舉一個簡單的例子吧,使用根結點爲A,其左右子樹分別爲B、C的樹來講解版本(1)的執行過程。

(1)初始時,p =T,接着執行循環,若根結點非空,則<p,false>入棧,表示根結點p的右子樹還未遍歷,同時置p爲其左指針,遍歷左子樹。若根結點爲空(此時棧也爲空),循環條件不滿足,直接結束。

(2)在接下來的循環中,若p爲空則看棧頂結點狀態。若其右子樹未遍歷,則遍歷其右子樹(p值發生改變),否則直接輸出結點值,並彈出棧頂結點(p值未改變)。

(3)p=A,<A,false>入棧,p=A->lchild=B;

p=B,<B,false>入棧,p=NULL;

p=NULL,棧非空,且棧頂結點B的右子樹未訪問,置true,然後訪問B的右子樹,p=B->rchild=NULL;

p=NULL,棧非空,且棧頂結點B的右子樹已訪問,輸出結點B,彈出棧頂結點B,注意此時p==NULL,因爲這個過程p的值未改變。

p=NULL,棧非空,且此時棧頂結點A的右子樹未訪問,置true,然後訪問A的右子樹,p=A->rchild=C;

p=C,非空,<C,false>入棧,p = C->lchild=NULL;

p=NULL,棧非空,棧頂元素C的右子樹爲訪問,置true,然後訪問C的右子樹,p=C->rchild=NULL;

p=NULL,棧非空,棧頂元素C的右子樹已訪問,輸出結點C,彈出棧頂元素C。

p=NULL,棧非空,棧頂元素A的右子樹已訪問,輸出結點A,彈出棧頂元素A。

p=NULL,此時棧已爲空。

(4)注意到在上述的分析過程中,我們發現當p的值爲NULL,出棧,p的值未改變,此時我們不用判斷p的值,可以直接判斷棧頂元素的右子樹的訪問狀態,於是調整循環結構就得到了版本(2)。但是它們原理都是一樣的。

5、完整測試代碼

#include <cstdlib>
#include <iostream>
#include <stack>
#include <queue>

using namespace std;

//二叉樹定義 
typedef char ElementType;

typedef struct BiTreeNode
{
	ElementType data;
    struct BiTreeNode* lchild;
    struct BiTreeNode* rchild;
}BiTreeNode, *BiTree;

//遞歸的建立一棵二叉樹 
//輸入爲二叉樹的先序序列 
void createBiTree(BiTree &T)
{
	char data;
	data = getchar();
	if(data == '#')
	{
		T = NULL;
	}
	else
	{
		T = new BiTreeNode;
		T->data = data;
		createBiTree(T->lchild);
		createBiTree(T->rchild);
	}
}

//遞歸銷燬一棵二叉樹
void destroyBiTree(BiTree &T)
{
	if(T)
	{
		destroyBiTree(T->lchild);
		destroyBiTree(T->rchild);
		delete T;
		T = NULL;
	}
}

//二叉樹的層次遍歷,用到了隊列 
void levelOrderTraverse(const BiTree& T)
{
	queue<BiTree> q;
	BiTree p = NULL;
	
	if(T)//若根結點非空,則入隊列 
	{
		q.push(T);
	}
	while(!q.empty())//隊列非空 
	{
		p = q.front();
		q.pop();
		cout<<p->data<<" ";
		if(p->lchild)//左孩子不空,入隊列 
		{
			q.push(p->lchild);
		}
		if(p->rchild)//右孩子不空,入隊列 
		{
			q.push(p->rchild);
		}
	} 
}

//二叉樹的先序非遞歸遍歷 
void iterativePreOrderTraverse(const BiTree& T)
{
	stack<BiTree> s;
	BiTree p = T;
	
	while(p || !s.empty())//p非空,或棧非空
	{
		if(p)
		{
			//輸出根結點,根結點入棧,遍歷左子樹 
			cout<<p->data<<" ";
			s.push(p);
			p = p->lchild;
		}
		else
		{
			//根結點退棧,遍歷右子樹 
			p = s.top();
			s.pop();
			p = p->rchild;//轉右孩子結點 
		}
	}
}

//二叉樹的中序非遞歸遍歷 
void iterativeInOrderTraverse(const BiTree& T)
{
	stack<BiTree> s;
	BiTree p = T;
	
	while(p || !s.empty())//p非空,或棧非空
	{
		if(p)
		{
			//根指針進棧 ,遍歷左子樹 
			s.push(p);	
			p = p->lchild;
		}
		else
		{
			//根指針退棧,訪問根結點,遍歷右子樹 
			p = s.top();
			s.pop();
			cout<<p->data<<" ";
			p = p->rchild;
		}
	}
}

//二叉樹的後序非遞歸遍歷 
void iterativePostOrderTraverse(const BiTree& T)
{
	stack<pair<BiTree,bool> > s;
	BiTree p = T;
	
	while(p || !s.empty())
	{
		if(p)
		{
			s.push(make_pair(p,false));//false表示根結點p的右子樹未訪問 
			p = p->lchild;
		}
		else
		{
			if(s.top().second == false)//根結點的右子樹未訪問 
			{
				s.top().second = true;//標誌右子樹爲已訪問 
				p = s.top().first->rchild;//遍歷右子樹 
			}
			else//右子樹已訪問 
			{
				cout<<s.top().first->data<<" "; //輸出根結點值 
				s.pop();//根結點出棧 
			}
		}	
	}
}

//後序非遞歸遍歷的另一個版本 
void anotherIterativePostOrderTraverse(const BiTree& T)
{
	stack<pair<BiTree,bool> > s;
	BiTree p = T;
	
	do
	{
		while(p)
		{
			s.push(make_pair(p,false));
			p = p->lchild;
		}
		
		while(!s.empty() && s.top().second == true)
		{
			cout<<s.top().first->data<<" ";
			s.pop();
		}
		
		if(!s.empty())
		{
			s.top().second = true;
			p = s.top().first->rchild;
		}
	}while(!s.empty());
}

int main(int argc, char *argv[])
{
	BiTree T = NULL;
	
	createBiTree(T);//建立二叉樹 如輸入AB#D##CE### 
//	createBiTreeWithGenList(T);//如輸入A(B(,D),C(E))#

	cout<<"levelOrder: ";
	levelOrderTraverse(T);
	cout<<endl;
	
	cout<<"preOrder: "; //先序遍歷 
//	preOrderTraverse(T);
	iterativePreOrderTraverse(T);
	cout<<endl;
	
	cout<<"inOrder: ";//中序遍歷 
//	inOrderTraverse(T);
	iterativeInOrderTraverse(T);
	cout<<endl;
	
	cout<<"postOrder: ";//後序遍歷 
//	postOrderTraverse(T);
//	iterativePostOrderTraverse(T);
	anotherIterativePostOrderTraverse(T);
	cout<<endl;
	
	destroyBiTree(T);//銷燬二叉樹,釋放空間 
	
    system("PAUSE");
    return EXIT_SUCCESS;
}

注:測試代碼中的一些其他函數請看二叉樹的基本操作:http://blog.csdn.net/sysu_arui/article/details/7865876





發佈了38 篇原創文章 · 獲贊 10 · 訪問量 12萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章