日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 综合教程 >内容正文

综合教程

最优二叉树(赫夫曼树、赫夫曼树和赫夫曼编码的存储结构)

發(fā)布時(shí)間:2023/12/13 综合教程 23 生活家
生活随笔 收集整理的這篇文章主要介紹了 最优二叉树(赫夫曼树、赫夫曼树和赫夫曼编码的存储结构) 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

最優(yōu)二叉樹是帶權(quán)路徑長(zhǎng)度最短的二叉樹。根據(jù)結(jié)點(diǎn)的個(gè)數(shù)、權(quán)值的不同,最優(yōu)二叉
樹的形狀也各不相同。圖634 是3 棵最優(yōu)二叉樹的例子。它們的共同特點(diǎn)是:帶權(quán)值的
結(jié)點(diǎn)都是葉子結(jié)點(diǎn)。權(quán)值越小的結(jié)點(diǎn),其到根結(jié)點(diǎn)的路徑越長(zhǎng)。構(gòu)造最優(yōu)二叉樹的方法
如下:
(1) 將每個(gè)帶有權(quán)值的結(jié)點(diǎn)作為一棵僅有根結(jié)點(diǎn)的二叉樹,樹的權(quán)值為結(jié)點(diǎn)的權(quán)值;
(2) 將其中兩棵權(quán)值最小的樹組成一棵新二叉樹,新樹的權(quán)值為兩棵樹的權(quán)值之和;

(3) 重復(fù)(2),直到所有結(jié)點(diǎn)都在一棵二叉樹上。這棵二叉樹就是最優(yōu)二叉樹。


最優(yōu)二叉樹的左右子樹是可以互換的,因?yàn)檫@不影響樹的帶權(quán)路徑長(zhǎng)度。當(dāng)結(jié)點(diǎn)的權(quán)
值差別大到一定程度,最優(yōu)二叉樹就形成了如圖634(b)所示的“一邊倒”的形狀。有些
書稱最優(yōu)二叉樹都是這種“一邊倒”的形狀是不對(duì)的。這通過計(jì)算二叉樹的帶權(quán)路徑長(zhǎng)度
是否最短就可看出。當(dāng)所有結(jié)點(diǎn)的權(quán)值一樣,或其權(quán)值差別很小,最優(yōu)二叉樹就形成了如
圖634(c)所示的完全二叉樹的形狀。葉子結(jié)點(diǎn)的路徑長(zhǎng)度近似相等。
最優(yōu)二叉樹除了葉子結(jié)點(diǎn)就是度為2 的結(jié)點(diǎn),沒有度為1 的結(jié)點(diǎn)。這樣才使得樹的帶
權(quán)路徑長(zhǎng)度最短。根據(jù)二叉樹的性質(zhì)3,最優(yōu)二叉樹的結(jié)點(diǎn)數(shù)為葉子數(shù)的2 倍減1。

// c6-7.h 赫夫曼樹和赫夫曼編碼的存儲(chǔ)結(jié)構(gòu)(見圖6.35)
typedef struct
{
	unsigned int weight;
	unsigned int parent,lchild,rchild;
}HTNode,*HuffmanTree; // 動(dòng)態(tài)分配數(shù)組存儲(chǔ)赫夫曼樹
typedef char **HuffmanCode; // 動(dòng)態(tài)分配數(shù)組存儲(chǔ)赫夫曼編碼表

c6-7.h 定義的二叉樹結(jié)構(gòu)是我們?cè)谇斑厸]有討論過的,但它特別適合建立赫夫曼樹。
赫夫曼樹是由多棵二叉樹(森林)組合成而的一棵樹。這種二叉樹結(jié)構(gòu)既適合表示樹,也適
合表示森林。赫夫曼樹結(jié)點(diǎn)的結(jié)構(gòu)包括權(quán)值、雙親及左右孩子,雙親值為0 的是根結(jié)點(diǎn),
左右孩子值均為0 的是葉子結(jié)點(diǎn)。這種二叉樹結(jié)構(gòu)是動(dòng)態(tài)生成的順序結(jié)構(gòu)。當(dāng)葉子結(jié)點(diǎn)數(shù)
確定,赫夫曼樹的結(jié)點(diǎn)數(shù)也確定。由圖636(d)可見,建成的赫夫曼樹除0 號(hào)結(jié)點(diǎn)空間不
用外,每個(gè)結(jié)點(diǎn)空間都沒空置。

// func6-1.cpp 程序 algo6-1.cpp和algo6-2.cpp要調(diào)用
int min(HuffmanTree t,int i)
{ // 返回i個(gè)結(jié)點(diǎn)中權(quán)值最小的樹的根結(jié)點(diǎn)序號(hào),函數(shù)select()調(diào)用
	int j,flag;
	unsigned int k=UINT_MAX; // 取k為不小于可能的值(無符號(hào)整型最大值)
	for(j=1;j<=i;j++)
		if(t[j].weight<k&&t[j].parent==0) // t[j]是樹的根結(jié)點(diǎn)
			k=t[j].weight,flag=j;
		t[flag].parent=1; // 給選中的根結(jié)點(diǎn)的雙親賦1,避免第2次查找該結(jié)點(diǎn)
		return flag;
}
void select(HuffmanTree t,int i,int &s1,int &s2)
{ // 在i個(gè)結(jié)點(diǎn)中選擇2個(gè)權(quán)值最小的樹的根結(jié)點(diǎn)序號(hào),s1為其中序號(hào)小的那個(gè)
	int j;
	s1=min(t,i);
	s2=min(t,i);
	if(s1>s2)
	{
		j=s1;
		s1=s2;
		s2=j;
	}
}

// algo6-1.cpp 求赫夫曼編碼。實(shí)現(xiàn)算法6.12的程序
#include"c1.h"
#include"c6-7.h"
#include"func6-1.cpp"
void HuffmanCoding(HuffmanTree &HT,HuffmanCode &HC,int *w,int n) // 算法6.12
{ // w存放n個(gè)字符的權(quán)值(均>0),構(gòu)造赫夫曼樹HT,并求出n個(gè)字符的赫夫曼編碼HC
	int m,i,s1,s2,start;
	unsigned c,f;
	HuffmanTree p;
	char *cd;
	if(n<=1)
		return;
	m=2*n-1;
	HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode)); // 0號(hào)單元未用
	for(p=HT+1,i=1;i<=n;++i,++p,++w)
	{
		(*p).weight=*w;
		(*p).parent=0;
		(*p).lchild=0;
		(*p).rchild=0;
	}
	for(;i<=m;++i,++p)
		(*p).parent=0;
	for(i=n+1;i<=m;++i) // 建赫夫曼樹
	{ // 在HT[1~i-1]中選擇parent為0且weight最小的兩個(gè)結(jié)點(diǎn),其序號(hào)分別為s1和s2
		select(HT,i-1,s1,s2);
		HT[s1].parent=HT[s2].parent=i;
		HT[i].lchild=s1;
		HT[i].rchild=s2;
		HT[i].weight=HT[s1].weight+HT[s2].weight;
	}
	// 從葉子到根逆向求每個(gè)字符的赫夫曼編碼
	HC=(HuffmanCode)malloc((n+1)*sizeof(char*));
	// 分配n個(gè)字符編碼的頭指針向量([0]不用)
	cd=(char*)malloc(n*sizeof(char)); // 分配求編碼的工作空間
	cd[n-1]=''; // 編碼結(jié)束符
	for(i=1;i<=n;i++)
	{ // 逐個(gè)字符求赫夫曼編碼
		start=n-1; // 編碼結(jié)束符位置
		for(c=i,f=HT[i].parent;f!=0;c=f,f=HT[f].parent)
			// 從葉子到根逆向求編碼
			if(HT[f].lchild==c)
				cd[--start]='0';
			else
				cd[--start]='1';
			HC[i]=(char*)malloc((n-start)*sizeof(char));
			// 為第i個(gè)字符編碼分配空間
			strcpy(HC[i],&cd[start]); // 從cd復(fù)制編碼(串)到HC
	}
	free(cd); // 釋放工作空間
}
void main()
{
	HuffmanTree HT;
	HuffmanCode HC;
	int *w,n,i;
	printf("請(qǐng)輸入權(quán)值的個(gè)數(shù)(>1): ");
	scanf("%d",&n);
	w=(int*)malloc(n*sizeof(int));
	printf("請(qǐng)依次輸入%d個(gè)權(quán)值(整型):
",n);
	for(i=0;i<=n-1;i++)
		scanf("%d",w+i);
	HuffmanCoding(HT,HC,w,n);
	for(i=1;i<=n;i++)
		puts(HC[i]);
}

代碼的運(yùn)行結(jié)果(以教科書圖6.24 為例,如圖636 所示):

請(qǐng)輸入權(quán)值的個(gè)數(shù)(>1): 4
請(qǐng)依次輸入4個(gè)權(quán)值(整型):
7 5 2 4
0
10
110
111

圖636 是運(yùn)行過程的圖解。初始狀態(tài)下(見圖636(b)),權(quán)值分別為7、5、2、4
的4 個(gè)結(jié)點(diǎn)是4 棵獨(dú)立的樹(根結(jié)點(diǎn))。它們沒有雙親,也沒有左右孩子。反復(fù)查找權(quán)值最
小的兩棵樹,并把它們合并成一棵樹,其權(quán)值為兩樹的權(quán)值之和。最后,所有結(jié)點(diǎn)合并成
一棵赫夫曼樹(見圖636(d))。
算法6.12(在algo6-1.cpp 中)在找到兩個(gè)無雙親且權(quán)值最小的結(jié)點(diǎn)后,將序號(hào)小的結(jié)
點(diǎn)作為左子樹,序號(hào)大的結(jié)點(diǎn)作為右子樹,如果不按這個(gè)規(guī)則,赫夫曼編碼的形式會(huì)改
變。但碼長(zhǎng)不會(huì)改變,仍然是赫夫曼編碼。


// algo6-2.cpp 實(shí)現(xiàn)算法6.13的程序
#include"c1.h"
#include"c6-7.h"
#include"func6-1.cpp"
void HuffmanCoding(HuffmanTree &HT,HuffmanCode &HC,int *w,int n) // 前半部分為算法6.12
{ // w存放n個(gè)字符的權(quán)值(均>0),構(gòu)造赫夫曼樹HT,并求出n個(gè)字符的赫夫曼編碼HC
	int m,i,s1,s2; // 此句與algo6-1.cpp不同
	unsigned c,cdlen; // 此句與algo6-1.cpp不同
	HuffmanTree p;
	char *cd;
	if(n<=1)
		return;
	m=2*n-1;
	HT=(HuffmanTree)malloc((m+1)*sizeof(HTNode)); // 0號(hào)單元未用
	for(p=HT+1,i=1;i<=n;++i,++p,++w)
	{
		(*p).weight=*w;
		(*p).parent=0;
		(*p).lchild=0;
		(*p).rchild=0;
	}
	for(;i<=m;++i,++p)
		(*p).parent=0;
	for(i=n+1;i<=m;++i) // 建赫夫曼樹
	{ // 在HT[1~i-1]中選擇parent為0且weight最小的兩個(gè)結(jié)點(diǎn),其序號(hào)分別為s1和s2
		select(HT,i-1,s1,s2);
		HT[s1].parent=HT[s2].parent=i;
		HT[i].lchild=s1;
		HT[i].rchild=s2;
		HT[i].weight=HT[s1].weight+HT[s2].weight;
	}
	// 以下為算法6.13,無棧非遞歸遍歷赫夫曼樹,求赫夫曼編碼,以上同算法6.12
	HC=(HuffmanCode)malloc((n+1)*sizeof(char*));
	// 分配n個(gè)字符編碼的頭指針向量([0]不用)
	cd=(char*)malloc(n*sizeof(char)); // 分配求編碼的工作空間
	c=m;
	cdlen=0;
	for(i=1;i<=m;++i)
		HT[i].weight=0; // 遍歷赫夫曼樹時(shí)用作結(jié)點(diǎn)狀態(tài)標(biāo)志
	while(c)
	{
		if(HT[c].weight==0)
		{ // 向左
			HT[c].weight=1;
			if(HT[c].lchild!=0)
			{
				c=HT[c].lchild;
				cd[cdlen++]='0';
			}
			else if(HT[c].rchild==0)
			{ // 登記葉子結(jié)點(diǎn)的字符的編碼
				HC[c]=(char *)malloc((cdlen+1)*sizeof(char));
				cd[cdlen]='';
				strcpy(HC[c],cd); // 復(fù)制編碼(串)
			}
		}
		else if(HT[c].weight==1)
		{ // 向右
			HT[c].weight=2;
			if(HT[c].rchild!=0)
			{
				c=HT[c].rchild;
				cd[cdlen++]='1';
			}
		}
		else
		{ // HT[c].weight==2,退回
			HT[c].weight=0;
			c=HT[c].parent;
			--cdlen; // 退到父結(jié)點(diǎn),編碼長(zhǎng)度減1
		}
	}
	free(cd);
}
void main()
{ // 主程序同algo6-1.cpp
	HuffmanTree HT;
	HuffmanCode HC;
	int *w,n,i;
	printf("請(qǐng)輸入權(quán)值的個(gè)數(shù)(>1): ");
	scanf("%d",&n);
	w=(int *)malloc(n*sizeof(int));
	printf("請(qǐng)依次輸入%d個(gè)權(quán)值(整型):
",n);
	for(i=0;i<=n-1;i++)
		scanf("%d",w+i);
	HuffmanCoding(HT,HC,w,n);
	for(i=1;i<=n;i++)
		puts(HC[i]);
}

代碼的運(yùn)行結(jié)果(以教科書例62 為例):

請(qǐng)輸入權(quán)值的個(gè)數(shù)(>1): 8
請(qǐng)依次輸入8個(gè)權(quán)值(整型):
5 29 7 8 14 23 3 11
0110
10
1110
1111
110
00
0111
010

總結(jié)

以上是生活随笔為你收集整理的最优二叉树(赫夫曼树、赫夫曼树和赫夫曼编码的存储结构)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。