補充知識點:線索二叉樹
一、樹、二叉樹和森林互相轉換
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));
}