樹、二叉樹和森林互相轉換、哈夫曼樹、二叉樹遍歷強化

補充知識點:線索二叉樹

一、樹、二叉樹和森林互相轉換

1.1 樹轉二叉樹

樹中每個結點最多隻有一個最左邊的孩子(長子)和一個右鄰的兄弟。按照這種關係很自然地就能將樹轉換成相應的二叉樹:1.在所有兄弟結點之間加一連線2.對每個結點,除了保留與其長子的連線外,去掉該結點與其它孩子的連線。

也就是說,在轉換成二叉樹之後,除了根結點的每一個結點的右孩子時該結點的兄弟,左孩子是該結點的長子。

如下圖所示:
樹兄弟加線
給除長子外的孩子去線層次調整

1.2 二叉樹轉樹

是樹轉換爲二叉樹的逆過程。
1.加線。若某結點X的左孩子結點存在,則將這個左孩子的右孩子結點、右孩子的右孩子結點、右孩子的右孩子的右孩子結點…,都作爲結點X的孩子。將結點X與這些右孩子結點用線連接起來。
2.去線。刪除原二叉樹中所有結點與其右孩子結點的連線。

如下圖所示:
二叉樹加線去線在這裏插入圖片描述

1.3 森林轉二叉樹

具體的方法是:
1.將森林中的每棵樹變爲二叉樹;
2.因爲轉換所得的二叉樹的根結點的右子樹均爲空,故可將各二叉樹的根結點視爲兄弟從左至右連在一起,就形成了一棵二叉樹。

也就是說,根結點的右孩子是一棵樹,左孩子是長子,長子的右孩子是兄弟,長子的左孩子是孩子。

如下圖所示:
擁有三顆樹的森林在這裏插入圖片描述在這裏插入圖片描述

1.4 森林轉二叉樹

具體的方法是:
1.將森林中的每棵樹變爲二叉樹;
2.因爲轉換所得的二叉樹的根結點的右子樹均爲空,故可將各二叉樹的根結點視爲兄弟從左至右連在一起,就形成了一棵二叉樹。

如下圖所示:

擁有三棵樹的森林森林中每顆樹轉換爲二叉樹在這裏插入圖片描述

二、 哈夫曼樹

哈夫曼樹是一種帶權(W)路徑(P)長度(L)最短的二叉樹,也稱爲最優二叉樹。

2.1 創建哈夫曼樹

1,將所有左,右子樹都爲空的作爲根節點。
2,在森林中選出兩棵根節點的權值最小的樹作爲一棵新樹的左,右子樹,且置新樹的附加根節點的權值爲其左,右子樹上根節點的權值之和。注意,左子樹的權值應小於右子樹的權值。
3,從森林中刪除這兩棵樹,同時把新樹加入到森林中。
4,重複2,3步驟,直到森林中只有一棵樹爲止,此樹便是哈夫曼樹。

如下圖所示:
初始森林第一次合併二次合併哈夫曼樹

2.3 哈夫曼編碼

利用哈夫曼樹求得的用於通信的二進制編碼稱爲哈夫曼編碼。樹中從根到每個葉子節點都有一條路徑,對路徑上的各分支約定指向左子樹的分支表示”0”碼,指向右子樹的分支表示“1”碼,取每條路徑上的“0”或“1”的序列作爲各個葉子節點對應的字符編碼,即是哈夫曼編碼。

哈夫曼編樹(帶哈夫曼編碼)
上圖A,B,C,D的哈夫曼編碼爲:111,10,110,0

注意:設計電文總長度最短的二進制前綴編碼,就是以n個字符出現的頻率
作爲權構造一顆哈夫曼樹,由哈夫曼樹求得的編碼就是哈夫曼編碼。

參考博客(裏面有C#代碼實現):數據結構和算法系列16 哈夫曼樹

三、二叉樹遍歷上機任務

#include <stdio.h>
#include <stdlib.h>
typedef struct BiTNode{
  char data;
  struct BiTNode *lchild,*rchild;
}BiTNode,*BinTree;
int n0,n1,n2; //用全局變量分別統計葉子結點、單孩子結點、雙孩子結點個數

void CreateBiTree(BinTree *T);   //構造二叉鏈表
void InOrder(BinTree T);         //中序遍歷
void PostOrder(BinTree T);       //後序遍歷
int Leafnum(BinTree p);         //統計葉子結點數目
int Onechildnum(BinTree p);	 //統計單孩子結點數目
int Twochildnum(BinTree p);	 //統計雙孩子結點數目
void main()   //主函數
{
	BinTree T;
	char ch;

	while(1)
	{
		system("cls");
		printf("\n\t二叉樹的基本操作:\n");
		printf("\n\tA -----二叉樹建立-------");
		printf("\n\tB -----中序遍歷---------");
	    printf("\n\tc -----後序遍歷---------");
		printf("\n\td -----求葉子節點個數---------");
		printf("\n\te -----求單孩子節點個數---------");
		printf("\n\tf -----求雙孩子節點個數---------");
		printf("\n\n\t請選擇:");
		scanf("\n%c",&ch);

		switch(ch)
		{
			case 'a':
			case 'A':printf("\t請按先序輸入二叉樹存儲的結點序列:");
					 CreateBiTree(&T);
					 break;

			case 'b':
			case 'B':printf("\t該二叉樹的中序遍歷序列爲:");  //調用中序遍歷函數
					 InOrder(T);
					 break;

			case 'C':
			case 'c':printf("\t該二叉樹的後序遍歷序列爲:");  
					 PostOrder(T);
					 break;

			case 'd':
			case 'D':printf("\t該二叉樹的葉子節點數爲:%d",Leafnum(T));  
					 break;

			case 'e':
			case 'E':printf("\t該二叉樹的單孩子節點數爲:%d",Onechildnum(T));  
					 break;

			case 'f':
			case 'F':printf("\t該二叉樹的雙孩子節點數爲:%d",Twochildnum(T));  
					 break;
		
			default:printf("\n\t輸入錯誤,重新選擇");
		}
		printf("\n\t");
		system("pause");
	}
}
void CreateBiTree(BinTree *T)  //建立二叉鏈表
{ 
	char c;
	scanf("\n%c",&c);
	if(c=='0')
      *T=NULL;
	else
	{

	*T=new BiTNode; 
	(*T)->data =c;
	CreateBiTree(&(*T)->lchild);//構造左子樹
	CreateBiTree(&(*T)->rchild);//構造右子樹    


	}
}
void InOrder(BinTree T) //中序遍歷
{
if(T== NULL)
return;
  PostOrder(T->lchild);
 printf("%c",T->data);
 PostOrder(T->rchild);

 
}
void PostOrder(BinTree T) //後序遍歷
{
 if(T== NULL)
 return;

 PostOrder(T->lchild);
 PostOrder(T->rchild);
 printf("%c",T->data);
 
}



int Leafnum(BinTree p)  //計算二叉樹葉子結點數目
{ 
 
	if(p==NULL)
		return 0;
	if(p->lchild==NULL&&p->rchild==NULL)
		return 1;
	return (Leafnum(p->lchild)+Leafnum(p->rchild));
}

int Onechildnum(BinTree p)  //計算單孩子結點數目
{ 
  if(p==NULL)
  return 0;
   if((p->lchild !=NULL && p->rchild==NULL) || (p->lchild ==NULL && p->rchild!=NULL))
  return 1;
  return  (Onechildnum(p->lchild)+Onechildnum(p->rchild));

}
int Twochildnum(BinTree p)  //計算雙孩子結點數目
{ 

  if(p==NULL)
  return 0;
   if(p->lchild !=NULL && p->rchild!=NULL )
   return 1;
   return (Twochildnum(p->lchild)+Twochildnum(p->rchild));
}

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