0023算法笔记贪心算法哈夫曼编码问题docx.docx
《0023算法笔记贪心算法哈夫曼编码问题docx.docx》由会员分享,可在线阅读,更多相关《0023算法笔记贪心算法哈夫曼编码问题docx.docx(24页珍藏版)》请在冰豆网上搜索。
0023算法笔记贪心算法哈夫曼编码问题docx
0023算法笔记——【贪心算法】哈夫曼编码问题
1、问题描述
哈夫曼编码是广泛地用于数据文件压缩的十分有效的编码方法。
其压缩率通常在20%~90%之间。
哈夫曼编码算法用字符在文件中出现的频率表来建立一个用0,1串表示各字符的最优表示方式。
一个包含100,000个字符的文件,各字符出现频率不同,如下表所示。
有多种方式表示文件中的信息,若用0,1码表示字符的方法,即每个字符用唯一的一个0,1串表示。
若采用定长编码表示,则需要3位表示一个字符,整个文件编码需要300,000位;若采用变长编码表示,给频率高的字符较短的编码;频率低的字符较长的编码,达到整体编码减少的目的,则整个文件编码需要(45×1+13×3+12×3+16×3+9×4+5×4)×1000=224,000位,由此可见,变长码比定长码方案好,总码长减小约25%。
前缀码:
对每一个字符规定一个0,1串作为其代码,并要求任一字符的代码都不是其他字符代码的前缀。
这种编码称为前缀码。
编码的前缀性质可以使译码方法非常简单;例如001011101可以唯一的分解为0,0,101,1101,因而其译码为aabe。
译码过程需要方便的取出编码的前缀,因此需要表示前缀码的合适的数据结构。
为此,可以用二叉树作为前缀码的数据结构:
树叶表示给定字符;从树根到树叶的路径当作该字符的前缀码;代码中每一位的0或1分别作为指示某节点到左儿子或右儿子的“路标”。
从上图可以看出,表示最优前缀码的二叉树总是一棵完全二叉树,即树中任意节点都有2个儿子。
图a表示定长编码方案不是最优的,其编码的二叉树不是一棵完全二叉树。
在一般情况下,若C是编码字符集,表示其最优前缀码的二叉树中恰有|C|个叶子。
每个叶子对应于字符集中的一个字符,该二叉树有|C|-1个内部节点。
给定编码字符集C及频率分布f,即C中任一字符c以频率f(c)在数据文件中出现。
C的一个前缀码编码方案对应于一棵二叉树T。
字符c在树T中的深度记为dT(c)。
dT(c)也是字符c的前缀码长。
则平均
码长定义为:
使平均码长达到最小的前缀码编码方
案称为C的最优前缀码。
2、构造哈弗曼编码
哈夫曼提出构造最优前缀码的贪心算法,由此产生的编码方案称为哈夫曼编码。
其构造步骤如下:
(1)哈夫曼算法以自底向上的方式构造表示最优前缀码的二叉树T。
(2)算法以|C|个叶结点开始,执行|C|-1次的“合并”运算后产生最终所要求的树T。
(3)假设编码字符集中每一字符c的频率是f(c)。
以f为键值的优先队列Q用在贪心选择时有效地确定算法当前要合并的2棵具有最小频率的树。
一旦2棵具有最小频率的树合并后,产生一棵新的树,其频率为合并的2棵树的频率之和,并将新树插入优先队列Q。
经过n-1次的合并后,优先队列中只剩下一棵树,即所要求的树T。
构造过程如图所示:
具体代码实现如下:
(1)4d4.cpp,程序主文件
[cpp]viewplaincopy
1.
2.
3.
4.
5.
6.
7.
8.
9.
//4d4贪心算法哈夫曼算法#include"stdafx.h"#include"BinaryTree.h"#include"MinHeap.h"#includeusingnamespacestd;
constintN=6;
10.templateclassHuffman;
11.
12.template
13.BinaryTreeHuffmanTree(Typef[],intn);
14.
15.template
16.classHuffman17.{
18.friendBinaryTreeHuffmanTree(Type[],int);
19.public:
21.
20.operatorType()const
{
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
returnweight;
//private:
BinaryTreetree;
Typeweight;
};
intmain()
{
charc[]={'0','a','b','c','d','e','f'};intf[]={0,45,13,12,16,9,5};//下标从1开始BinaryTreet=HuffmanTree(f,N);
cout<<"各字符出现的对应频率分别为:
"<{
cout<"<}
cout<cout<<"生成二叉树的前序遍历结果为:
"<cout<cout<<"生成二叉树的中序遍历结果为:
"<cout<t.DestroyTree();
return0;
}
template
BinaryTreeHuffmanTree(Typef[],intn)
{
//生成单节点树
Huffman*w=newHuffman[n+1];
BinaryTreez,zero;
for(inti=1;i<=n;i++)
{
z.MakeTree(i,zero,zero);w[i].weight=f[i];
w[i].tree=z;
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.}
*Childr=NULL)
//建优先队列
MinHeap>Q(n);for(inti=1;i<=n;i++)Q.Insert(w[i]);
//反复合并最小频率树
Huffmanx,y;
for(inti=1;i{
x=Q.RemoveMin();
y=Q.RemoveMin();
z.MakeTree(0,x.tree,y.tree);
x.weight+=y.weight;
x.tree=z;
Q.Insert(x);
}
x=Q.RemoveMin();
delete[]w;
returnx.tree;
(2)BinaryTree.h二叉树实现
[cpp]viewplaincopy
1.#include
2.usingnamespacestd;
3.
4.template
5.structBTNode
6.{
7.Tdata;
8.BTNode*lChild,*rChild;
9.
10.BTNode()
11.{
12.lChild=rChild=NULL;
13.}
14.
15.BTNode(constT&val,BTNode*Childl=NULL,BTNode
16.{
17.
data=val;
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
lChild=Childl;
rChild=Childr;
BTNode*CopyTree()
{
BTNode*nl,*nr,*nn;
if(&data==NULL)
returnNULL;
nl=lChild->CopyTree();
nr=rChild->CopyTree();
nn=newBTNode(data,nl,nr);
returnnn;
}
};
template
classBinaryTree
{
public:
BTNode*root;
BinaryTree();
~BinaryTree();
voidPre_Order();
voidIn_Order();
voidPost_Order();
intTreeHeight()const;
intTreeNodeCount()const;
voidDestroyTree();
voidMakeTree(TpData,BinaryTreeleftTree,BinaryTreerightTree
);
voidChange(BTNode*r);
private:
voidDestroy(BTNode*&r);
voidPreOrder(BTNode*r);
voidInOrder(BTNode*r);
61.voidPostOrder(BTNode*r);
62.
63.intHeight(constBTNode*r)const;
64.intNodeCount(constBTNode*r)const;
65.};
66.
67.template
68.BinaryTree:
:
BinaryTree()
69.{
70.root=NULL;
71.}
72.
73.template
74.BinaryTree:
:
~BinaryTree()
75.{
76.
77.}
78.
79.template
80.voidBinaryTree:
:
Pre_Order()
81.{
82.PreOrder(root);
83.}
84.
85.template
86.voidBinaryTree:
:
In_Order()
87.{
88.InOrder(root);
89.}
90.
91.template
92.voidBinaryTree:
:
Post_Order()
93.{
94.PostOrder(root);
95.}
96.
97.template
98.intBinaryTree:
:
TreeHeight()const
99.{
100.returnHeight(root);
101.}
102.
103.template
104.intBinaryTree:
:
TreeNodeCount()const
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
147.
148.
returnNodeCount(root);
}
template
voidBinaryTree:
:
DestroyTree()
{
Destroy(root);
}
template
voidBinaryTree:
:
PreOrder(BTNode*r)
{
if(r!
=NULL)
{
cout<data<<'';PreOrder(r->lChild);PreOrder(r->rChild);
}
}
template
voidBinaryTree:
:
InOrder(BTNode*r)
{
if(r!
=NULL)
{
InOrder(r->lChild);cout<data<<'';
InOrder(r->rChild);}
}
template
voidBinaryTree:
:
PostOrder(BTNode*r)
{
if(r!
=NULL)
{
PostOrder(r->lChild);
PostOrder(r->rChild);cout<data<<'';
}
}
template
149.
150.
151.
152.
153.
154.
155.
156.
157.
158.
159.
160.
161.
162.
163.
164.
165.
166.
167.
168.
169.
170.
171.
172.
173.
174.
175.
176.
177.
178.
179.
180.
181.
182.
183.
184.
185.
186.
187.
188.
189.
190.
191.
192.
intBinaryTree:
:
NodeCount(constBTNode*r)const
{
if(r==NULL)
return0;
else
return1+NodeCount(r->lChild)+NodeCount(r->rChild);
}
template
intBinaryTree:
:
Height(constBTNode*r)const
{
if(r==NULL)
return0;
else
{
intlh,rh;
lh=Height(r->lChild);
rh=Height(r->rChild);
return1+(lh>rh?
lh:
rh);
}
}
template
voidBinaryTree:
:
Destroy(BTNode*&r)
{
if(r!
=NULL)
{
Destroy(r->lChild);
Destroy(r->rChild);
deleter;
r=NULL;
}
}
template
voidBinaryTree:
:
Change(BTNode*r)//将二叉树bt所有结点的左右子树交换
{
BTNode*p;
if(r){
p=r->lChild;r->lChild=r->rChild;
r->rChild=p;//左右子女交换
Change(r->lChild);//交换左子树上所有结点的左右子树
Change(r->rChild);//交换右子树上所有结点的左右子树
194.}
195.
196.template
197.voidBinaryTree:
:
MakeTree(TpData,BinaryTreeleftTree,BinaryTreerightTree)
198.
{
199.
root=newBTNode();
200.
root->data=pData;
201.
root->lChild=leftTree.root;
202.
root->rChild=rightTree.root
203.
}
(3)MinHeap.h最小堆实现
[cpp]viewplaincopy
1.#include
2.usingnamespacestd;
3.template
4.classMinHeap
5.{
6.private:
7.T*heap;//元素数组,0号位置也储存元素
8.intCurrentSize;//目前元素个数
9.intMaxSize;//可容纳的最多元素个数
10.
11.voidFilterDown(constintstart,constintend);//自上往下调整,使关键字小的节点在上
12.voidFilterUp(intstart);//自下往上调整
13.
14.public:
15.MinHeap(intn=1000);
16.~MinHeap();
17.boolInsert(constT&x);//插入元素
18.
19.TRemoveMin();//删除最小元素
20.TGetMin();//取最小元素
21.
22.
23.
24.
25.};
26.
boolIsEmpty()const;boolIsFull()const;voidClear();
27.template
28.MinHeap:
:
MinHeap(intn)
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
{
MaxSize=n;
heap=newT[MaxSize];
CurrentSize=0;
}
template
MinHeap:
:
~MinHeap()
{
delete[]heap;
}
template
voidMinHeap:
:
FilterUp(intstart)//自下往上调整
{
intj=start,i=(j-1)/2;//i指向j的双亲节点
Ttemp=heap[j];
while(j>0)
{
if(heap[i]<=temp)
break;
else
{
heap[j]=heap[i];
j=i;
i=(i-1)/2;
}
}heap[j]=temp;
}
template
voidMinHeap:
:
FilterDown(constintstart,constintend)//自上往下调整,使关键字小的节点在上
{
inti=start,j=2*i+1;
Ttemp=heap[i];
while(j<=end)
{
if((jheap[j+1]))j++;
if(temp<=heap[j])break;
77.
78.}
79.heap[i]=temp;
80.}
81.
82.template
83.boolMinHeap:
:
Insert(constT&x)
84.{
85.if(CurrentSize==MaxSize)
86.returnfalse;
87.
88.heap[CurrentSize]=x;
89.FilterUp(CurrentSize);
90.
91.CurrentSize++;
92.returntrue;
93.}
94.
95.template
96.TMinHeap:
:
RemoveMin()
97.{
98.Tx=heap[0];
99.heap[0]=heap[CurrentSize-1];
100.
101.CurrentSize--;
102.FilterDown(0,CurrentSize-1);//调整新的根节点
103.
104.returnx;
105.}
106.
107.template
108.TMinHeap:
:
GetMin()
109.{
110.returnheap[0];
111.}
112.
113.template
114.boolMinHeap:
:
IsEmpty()const
115.{
116.
returnCurrentSize==0;
117.
118.
}
119.
template
120.
boolMinHeap:
:
IsFull()const
121.
{
122.
returnCurrentSize==MaxSize;
123.
124.
}
125.
template
126.
voidMinHeap:
:
Clear()
127.
{
128.
CurrentSize=0;
129.
}
3、贪心选择性质
二叉树T表示字符集C的一个最优前缀码,证明可以对T作适当修改后得到一棵新的二叉树T”,在T”中x和y是最深叶子且为兄弟,同时T”表示的前缀码也是C的最优前缀码。
设b和c是二叉树T的最深叶子,且为兄弟。
设f(b)<=f(c),f(x)<=f(y)。
由于x和y是C中具有最小频率的两个字符,有f(x)<=f(b),f(y)<=f(c)。
首先,在树T中交换叶子b和x的位置得到T',然后再树T'中交换叶子c和y的