给定字符集 C = { x 1 , x 2 , x 3 , . . . , x n } C=\{x_1,x_2,x_3,...,x_n\} C={x1,x2,x3,...,xn}和每个字符的频率 f ( x i ) {f(x_i)} f(xi) ,求关于 C 的一个最优前缀码
前缀码:是在有效字符前加的通用型代码。任何一个字符的编码都不能是其他字符编码的前缀,此即前缀码特性。具有前缀码特性的编码即为前缀码(名字有歧义)。
最优前缀码:对于编码字符集C,使平均码长达到最小的前缀码编码方案。
(前缀码和最优前缀码定义来源于百度百科)
思路:贪心构造最优前缀码
具体实现:构造最小堆,先后提取堆顶的两个节点,构造一个新节点,节点的值是之前两个节点值之和,新节点的左儿子是第一个节点,右儿子是第二个节点,再将原来的堆顶两个节点弹出堆,将新节点加入,如此直到最小堆的大小为1,即哈夫曼森林变成哈夫曼树,最后输出对应编码即可。
例子:
有C={A,B,C,D,E,F,G},对应频率为f(x)={3,6,7,4,2,20,1}
由图中易得:A:1010 B:110 C:100 D:111 E:10110 F:0 G:10111
class node //节点
{
public:
node* left, * right; //左右儿子
int val, ID; //频率和对应字符
string code; //编码
node(int val, int ID) //构造函数1
{
this->ID = ID;
this->val = val;
this->left = this->right = nullptr;
this->code = "";
}
node(int val, node* L, node* R) //构造函数2
{
this->val = val;
this->left = L;
this->right = R;
code = "";
}
bool morethan(const node* p) const //定义大于规则
{
return this->val > p->val;
}
};
class nodeCompare //优先队列的指针排序规则
{
public:
nodeCompare() {}
bool operator () (const node* p1, const node* p2) const
{
return p1->morethan(p2);
}
};
priority_queue<node*,vector<node*>,nodeCompare>que;//优先队列
void TravelTree(node* root)
{
if (root)
{
if (root->left)root->left->code = root->code + "1";//左叶子节点的编码修改
if (root->right)root->right->code = root->code + "0";//右叶子节点的编码修改
if (!root->left && !root->right)printf("%d %s\n", root->ID, root->code.c_str());//叶子节点输出编码
TravelTree(root->left);
TravelTree(root->right);
}
}
node* createHuffmanTree(int fqy[], int n)//创建哈夫曼树
{
for (int i = 1; i <= n; i++)
{
node* tmp = new node(fqy[i - 1], i);
que.push(tmp);
}
while (que.size() != 1) //贪心构造哈夫曼森林,最后形成哈夫曼树
{
node* L = que.top();
que.pop();
node* R = que.top();
que.pop();
node* newNode = new node(L->val + R->val, L, R);
que.push(newNode);
}
TravelTree(que.top());
return nullptr;
}
构建最小堆O(nlogn),遍历整个频率序列O(n),插入最小堆O(logn),删除堆顶元素O(logn),构造哈夫曼编码O(n),所以总的时间复杂度为O(nlogn)
哈夫曼算法的正确性证明,实质上也就是证明一棵最优树,最小的两个缩成一个,同时删除最小的两个,得到的新树还是最优树
证明:采用反证法
首先假定 T 是一颗最优树,T这棵树的权值记为W(T)。最小权值的两个A,B一定在最下层,对应的路径权值分别是 Wa, Wb。
倘若删除A和B,A和B的父节C成为一个叶子节点。此时新树为 T1,W(T) = W(T1)+Wa + Wb
倘若T1不是最优树,则必定有最优树 T11。因为T11是最优树,所以必定有W(T11)<= W(T1) ,
此时若把T11中的C展开成A和B,形成新树 T111 ,W(T111)= W(T11)+ Wa + Wb
上述四个关系是矛盾,所以,T1 一定是最优树得证。(证明转载自大佬)
Github