1. 程式人生 > >資料結構(四)二叉樹的遍歷

資料結構(四)二叉樹的遍歷

二叉樹的遍歷

0. 樹的表示

typedef struct TreeNode *BinTree;
struct TreeNode{
	int Data;  // 存值 
	BinTree Left;    // 左兒子結點 
	BinTree Right;   // 右兒子結點 
};

1. 先序遍歷

遍歷過程:

  1. 訪問根結點
  2. 先序遍歷其左子樹
  3. 先序遍歷其右子樹

1. 遞迴實現

void  PreOrderTraversal(BinTree BT){
	if(BT){
		printf("%d",BT->Data);  // 列印根 
		PreOrderTraversal(BT->Left)
; // 進入左子樹 PreOrderTraversal(BT->Right); // 進入右子樹 } }

2. 非遞迴實現

void PreOrderTraversal(BinTree BT){
	BinTree T = BT;
	Stack S = CreateStack();  // 建立並初始化堆疊 S
	while(T || !IsEmpty(S)){  // 當樹不為空或堆疊不空 
		while(T){     
			Push(S,T);    // 壓棧,第一次遇到該結點 
			printf("%d",T->Data);  // 訪問結點
			T = T->Left;
// 遍歷左子樹 } if(!IsEmpty(S)){ // 當堆疊不空 T = Pop(S); // 出棧,第二次遇到該結點 T = T->Right; // 訪問右結點 } } }

2. 中序遍歷

遞迴過程:

  1. 中序遍歷其左子樹
  2. 訪問根結點
  3. 中序遍歷其右子樹

1. 遞迴實現

void InOrderTraversal(BinTree BT){
	if(BT){
		InOrderTraversal(BT->Left);  // 進入左子樹 
		printf("%d",BT->Data);  // 列印根 
		InOrderTraversal
(BT->Right); // 進入右子樹 } }

2. 非遞迴實現

void InOrderTraversal(BinTree BT){
	BinTree T = BT;
	Stack S = CreateStack();  // 建立並初始化堆疊 S
	while(T || !IsEmpty(S)){  // 當樹不為空或堆疊不空 
		while(T){     
			Push(S,T);    // 壓棧
			T = T->Left;   // 遍歷左子樹 
		}
		if(!IsEmpty(S)){  // 當堆疊不空 
			T = Pop(S);    // 出棧
			printf("%d",T->Data);  // 訪問結點
			T = T->Right;  // 訪問右結點 
		}
	} 
} 

3. 後序遍歷

遍歷過程:

  1. 後序遍歷其左子樹
  2. 後序遍歷其右子樹
  3. 訪問根結點

1. 遞迴實現

void PostOrderTraversal(BinTree BT){
	if(BT){
		PostOrderTraversal(BT->Left);  // 進入左子樹 
		PostOrderTraversal(BT->Right);  // 進入右子樹 
		printf("%d",BT->Data);  // 列印根 
	} 
}

2. 非遞迴實現

void PostOrderTraversal(BinTree BT){
	BinTree T = BT;
	Stack S = CreateStack();  // 建立並初始化堆疊 S
	vector<BinTree> v;
	Push(S,T);
	while(!IsEmpty(S)){  // 當樹不為空或堆疊不空 
		T = Pop(S);
		v.push_back(T);
		if(T->Left)
			Push(S,T->Left);
		if(T->Right)
			Push(S,T->Right);
	}
	reverse(v.begin(),v.end());  // 逆轉 
	for(int i=0;i<v.size();i++)
		printf("%d",v[i]->Data);
} 

4. 總結

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

  • 先序遍歷是第一次"遇到"該結點時訪問
  • 中序遍歷是第二次"遇到"該結點(此時該結點從左子樹返回)時訪問
  • 後序遍歷是第三次"遇到"該結點(此時該結點從右子樹返回)時訪問

5. 層序遍歷

遍歷過程:從上至下,從左至右訪問所有結點

佇列實現過程:

  1. 從佇列中取出一個元素
  2. 訪問該元素所指結點
  3. 若該元素所指結點的左孩子結點非空,左孩子結點入隊
  4. 若該元素所指結點的右孩子結點非空,右孩子結點入隊

6. 例子

1. 輸出葉子結點

前序遍歷加個沒有孩子結點的約束即可

void  FindLeaves(BinTree BT){
	if(BT){
		if( !BT->Left && !BT->Right)
			printf("%d",BT->Data);  // 列印葉子結點
		FindLeaves(BT->Left);  // 進入左子樹 
		FindLeaves(BT->Right);  // 進入右子樹 
	}
} 

2. 樹的高度

當前樹的高度為其子樹最大高度 +1

int  GetHeight(BinTree BT){
	int hl,hr,maxh;
	if(BT){
		hl = GetHeight(BT->Left);  // 求左子樹高度 
		hr = GetHeight(BT->Right);  // 求右子樹高度 
		maxh = (hl>hr)?hl:hr;
		return maxh+1;  // 當前結點高度為左右子樹最大的高度+1 
	}else
		return 0;
} 

3. 由兩種遍歷序列確定二叉樹

前提:有一種序列必須是中序!

方法:

  1. 根據先序(或後序)遍歷序列第一個(或最後一個)結點確定根結點
  2. 根據根結點在中序序列中分割出左右兩個子序列
  3. 對左子樹和右子樹分別遞迴使用同樣的方法繼續分解