數據結構(三)二叉樹的遍歷

  二叉樹的遍歷可以分爲遞歸遍歷非遞歸遍歷層序遍歷。其本質是如何把二維結構變成一維線性結構的過程。

遞歸遍歷

  在遞歸遍歷中又可以分爲三種:先序遍歷中序遍歷後序遍歷

先序遍歷

  先序遍歷的遍歷過程爲:

  1. 訪問根節點
  2. 先序遍歷其左子樹
  3. 先序遍歷其右子樹
void PreOrderTraversal ( BinTree BT )
{
	if(BT){ // 判斷節點是否爲空
	printf ("%d'”,BT->Data) ;
	PreOrderTraversal ( BT->Left ) ;
	PreOrderTraversal ( BT->Right );
	}
}

  對於下圖中的樹,其先序遍歷結果爲:A B D F E C G H I。

例圖

中序遍歷

  中序遍歷的遍歷過程爲:

  1. 中序遍歷其左子樹;
  2. 訪問根結點;
  3. 中序遍歷其右子樹
void PreOrderTraversal ( BinTree BT )
{
	if(BT){ // 判斷節點是否爲空
	PreOrderTraversal ( BT->Left ) ;
	printf ("%d'”,BT->Data) ;
	PreOrderTraversal ( BT->Right );
	}
}

  對於下圖中的樹,其先序遍歷結果爲:D B E F A G H C I。

例圖

後序遍歷

  後序遍歷的遍歷過程爲:

  1. 後序遍歷其左子樹;
  2. 後序遍歷其右子樹;
  3. 訪問根結點。.
void PreOrderTraversal ( BinTree BT )
{
	if(BT){ // 判斷節點是否爲空
	PreOrderTraversal ( BT-> Left ) ;
	PreOrderTraversal ( BT-> Right );
	printf ("%d'”,BT->Data) ;
	}
}

  對於下圖中的樹,其先序遍歷結果爲:D E F B H G I C A。

例圖

  先序、中序和後序遍歷過程:遍歷過程中經過結點的路線一樣,只是訪問各結點的時機不同

  下圖中在從入口到出口的曲線上用×號星號三角三種符號分別標記出了先序、中序和後序訪問各結點的時刻:

先序、中序、後序彙總

非遞歸遍歷

  上述方法是使用遞歸的算法對其進行求解,我們知道遞歸的方法算法空間複雜度較高,如果採用非遞歸的方法,我們可以使用堆棧的方法。

中序遍歷非遞歸遍歷算法

  中序遍歷非遞歸遍歷算法主要可以分爲以下三步:

  1. 遇到一個結點,就把它壓棧,並去遍歷它的左子樹
  2. 左子樹遍歷結束後,從棧頂彈出這個結點並訪問它
  3. 然後按其右指針再去中序遍歷該結點的右子樹

  其程序如下:

void InOrderTraversal ( BinTree BT )
{ 	BinTree T = BT;
	stack s = Creatstack( Maxsize ); /* 創建並初始化堆棧s*/
	while( T || !IsEmpty(S) ) {
		while (T) { ( /*--直向左並將沿途結點壓入堆棧*/
			Push(S,T) ;
			T = T->Left;
		}
		if(!IsEmpty (S) )T
			T = Pop(s); /*結點彈出堆棧*/
			printf ("%5d", T->Data); /* (訪問)打印結點*/
			T = T->Right; /*轉向右子樹*/
		}
	}
}

  若想要把其改爲先序遍歷的非遞歸算法,只需把printf語句放到while(T)下面這一行:

void InOrderTraversal ( BinTree BT )
{ 	BinTree T = BT;
	stack s = Creatstack( Maxsize ); /* 創建並初始化堆棧s*/
	while( T || !IsEmpty(S) ) {
		while (T) { ( /*--直向左並將沿途結點壓入堆棧*/
			printf ("%5d", T->Data); /* (訪問)打印結點*/
			Push(S,T) ;
			T = T->Left;
		}
		if(!IsEmpty (S) )T
			T = Pop(s); /*結點彈出堆棧*/
			T = T->Right; /*轉向右子樹*/
		}
	}
}

層序遍歷

  二叉樹遍歷的核心問題就是:二維結構的線性化。其實我們只需要一個存儲結構來保存暫時不訪問的結點就可以了,因此除了堆棧,我們還可以用隊列的方式對其進行存儲。

  隊列實現:遍歷從根結點開始,首先將根結點入隊,然後開始執行循環:結點出隊、訪問該結點、其左右兒子入隊。

層序遍歷

  層序遍歷基本過程:先根結點入隊,然後:

  1. 從隊列中取出一個元素;
  2. 訪問該元素所指結點;
  3. 若該元素所指結點的左、右孩子結點非空,則將其左、右孩子的指針順序入隊
void Leve lOrderTraversal ( BinTree BT )
{ Queue Q ; BinTree T ;
	if ( !BT ) return; /* 若是空樹則直接返回*/
	Q =CreatQueue( Maxsize ); /*創建並初始化隊列0*/
	AddQ(Q,BT);
	while(!IsEmptyQ(Q)){
		T=DeleteQ(Q);
		printf ("&d\n", T->Data) ; /*訪 問取出隊列的結點*/
		if ( T->Left ) AddQ( Q,T->Left ) ;
		if ( T->Right ) AddQ( Q,T->Right ) ;
	}
}

應用

  • 輸出二叉樹中的葉子結點

  遍歷二叉樹的應用:輸出二叉樹中的葉子結點。在二叉樹的遍歷算法中增加檢測結點的“左右子樹是否都爲空”。

void PreOrderPrintLeaves( BinTree BT )
{
	if(BT) {
		if ( !BT-Left && !BT- >Right )
			printf ("8d",BT- >Data ) ;
		PreOrderPrintLeaves ( BT- >Left ) ;
		PreOrderPrintLeaves ( BT- >Right ) ;
	}
}
  • 求二叉樹的高度

二叉樹的高度

int PostorderGetHeight( BinTree BT )
{	int HL, HR,MaxH;
	if(BT){
		Hi = PostorderGetHeight (BT->Ieft) ; /*求左子樹的深度*/
		HR = PostorderGetHeight (BT->Right) ; /*求右子樹的深度*/
		MaxH = (HI > HR) ? HL : HR; /*取左右子樹較大的深度*/
		return ( MaxH + 1 ); /*返回樹的深度*/
	}
	else return 0; /*空樹深度爲0 */
}

  如果想要由兩種遍歷序列確定二叉樹,必須要有中序遍歷才行。

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