哈夫曼编码
在进行信息传输时,我们通常需要将字符串转化为一串由0和1构成编码。
不难想到,我们可以将每一个字符ASCLL码值的二进制编码作为转化后的编码。每个字符对应了八位数字,这就会导致最后传输的数据十分长。
因此,我们需要一种更好的编码方式——哈夫曼编码。与二进制编码不同的是,每个字符的编码是不定长的,这就给予了编码一定的灵活性。在哈夫曼编码过程中,会给每一个字符赋予一个特殊的权值。一般来说,这个权值可以根据字符出现的频次来进行设定,出现频次越高的字符,我们就使其长度越短,这就保证了编码总长度最短。
如,我们对{"a":1,"b":5,"c":3,"d":11,"e":4,"f":17}进行哈夫曼编码时,得到了{100,101,110,00,111,01}这样一组哈夫曼编码(哈夫曼编码并不唯一),我们可以发现,由于其中任何一个字符编码都不会是另一个字符编码的前缀,因此每一个字符编码都可以被唯一地识别,这也确保了哈夫曼编码的准确性。
哈夫曼树
树的带权路径长度之和(WPL)为其所有叶结点的带权路径长度总和,则对于权值为W,所在层数为L(根节点层数认为是0)的每一个i号叶结点,树的WPL值为:
而哈夫曼树则是由同一组叶结点创建的所有树中,WPL值最小的二叉树。
创建哈夫曼树的过程中,我们每一次需要选择两个权值最小的结点,并将他们组成一个新的父结点,父结点权值为子结点权值之和。同时,通过这种方式,我们为这两个子结点分别赋予了相应的编码,编码规则为左0右1或左1右0。这样,当我们将整棵哈夫曼树建立起来之后,从根结点到相应叶结点经过的路径则对应了其哈夫曼编码。
堆优化哈夫曼树的代码实现
不难发现,哈夫曼的创建过程需要不断选取当前权值最小的结点,因此,我们可以使用小根堆来优化这个过程。以下则是相应部分的代码实现:
priority_queue.h
#ifndef _priority_queue_H
#define _priority_queue_H
#define lc(x) x << 1 | 1
#define rc(x) (x << 1) + 2
//仿函数
template <class T>
class Less
{
public:
bool operator()(T &a, T &b)
{
return a < b;
}
};
template <class T, class ORD = Less<T>>
class priority_queue
{
T *arr;
size_t size, capacity;
//扩容
void Extend()
{
size_t new_capacity = capacity + (((capacity >> 1) > 1) ? (capacity >> 1) : 1);
T *p = (T*)realloc(arr, new_capacity * sizeof(T));
if (!p)
{
std::cout << "malloc error." << std::endl;
return;
}
capacity = new_capacity;
arr = p;
p = nullptr;
}
//从子结点依次向上排序
void PushUp(size_t cur)
{
if (!cur)
return;
size_t pa;
if (cur & 1)
pa = (cur - 1) >> 1;
else
pa = (cur - 2) >> 1;
if (ORD()(arr[cur], arr[pa]))
{
T t = arr[cur];
arr[cur] = arr[pa];
arr[pa] = t;
PushUp(pa);
}
}
//从父节点依次向下排序
void PushDown(size_t cur)
{
if (lc(cur) >= size)
return;
int ne = lc(cur);
if (rc(cur) < size && ORD()(arr[rc(cur)], arr[lc(cur)]))
ne = rc(cur);
if (!ORD()(arr[ne], arr[cur]))
return;
T t = arr[cur];
arr[cur] = arr[ne];
arr[ne] = t;
PushDown(ne);
}
public:
//获取堆元素个数
int Size() const
{
return size;
}
//判空
bool Empty() const
{
return size == 0;
}
//获取堆顶元素
T top() const
{
return arr[0];
}
//将元素压入堆
void push(const T x)
{
if (size >= capacity)
Extend();
arr[size++] = x;
size_t cur = size - 1;
PushUp(cur);
}
//从堆中弹出元素
void pop()
{
arr[0] = arr[--size];
PushDown(0);
}
//遍历并打印所有元素(层序)
void _travel()
{
for (int i = 0; i < size; i++)
std::cout << arr[i] << ' ';
std::cout << std::endl;
}
//构造函数
priority_queue() : arr(nullptr), size(0), capacity(1)
{
arr = (T *)malloc(sizeof(T));
}
//析构函数
~priority_queue()
{
if (arr)
free(arr);
}
};
#endif
trnode.h
#ifndef _trnode_H
#define _trnode_H
#include <optional>
template <class T>
class TrNode
{
public:
std::optional<T> e;
TrNode<T>* ch[2];
int weight;
//重载小于号
bool operator<(const TrNode &b);
TrNode();
TrNode(std::optional<T> elem, int w);
~TrNode() = default;
};
//重载小于号
template <class T>
bool TrNode<T>::operator<(const TrNode &b)
{
return this->weight < b.weight;
}
template <class T>
TrNode<T>::TrNode() : e(std::nullopt), weight(0)
{
ch[0] = nullptr;
ch[1] = nullptr;
}
template <class T>
TrNode<T>::TrNode(std::optional<T> elem, int w) : e(elem), weight(w)
{
ch[0] = nullptr;
ch[1] = nullptr;
}
#endif
huffman_tree.h
#ifndef _huffman_tree_H
#define _huffman_tree_H
#include "trnode.h"
#include "priority_queue.h"
#include <vector>
#include <map>
#include <iostream>
template <class T>
class HuffmanTree
{
static priority_queue<TrNode<T> *> *q;
TrNode<T> *arr;
int idx, size;
std::map<std::optional<T>, std::string> encode_mp;
std::map<std::string, std::optional<T>> decode_mp;
void Update(TrNode<T> *cur, std::string &str);
void CreateHuffmanTree();
public:
std::string CreateCode(std::optional<T> e);
std::string CreateCompleteCode(std::vector<std::optional<T>> v) const;
std::string CreateCompleteCode(std::string str);
void PrintAllCodes();
void PrintAllCodes(std::vector<T> &elems);
std::string Decode(std::string str);
HuffmanTree(int n, std::vector<T> &elems, std::vector<int> &weights);
~HuffmanTree();
};
template <class T>
priority_queue<TrNode<T> *> *HuffmanTree<T>::q = new priority_queue<TrNode<T> *>();
// dfs更新map对应值
template <class T>
void HuffmanTree<T>::Update(TrNode<T> *cur, std::string &str)
{
if (!cur->ch[0] && !cur->ch[1])
{
encode_mp[cur->e] = str;
decode_mp[str] = cur->e;
return;
}
if (cur->ch[0])
{
str.push_back('0');
Update(cur->ch[0], str);
str.pop_back();
}
if (cur->ch[1])
{
str.push_back('1');
Update(cur->ch[1], str);
str.pop_back();
}
}
//生成哈夫曼树
template <class T>
void HuffmanTree<T>::CreateHuffmanTree()
{
for (int i = 1; i <= size; i++)
{
q->push(&arr[i]);
}
while (q->Size() > 1)
{
auto a = q->top();
q->pop();
auto b = q->top();
q->pop();
arr[++idx] = TrNode{(std::optional<T>)std::nullopt, a->weight + b->weight};
arr[idx].ch[0] = a;
arr[idx].ch[1] = b;
q->push(&arr[idx]);
}
std::string s = "";
Update(arr + idx, s);
}
//为一个元素编码
template <class T>
std::string HuffmanTree<T>::CreateCode(std::optional<T> e)
{
return encode_mp[e];
}
//为一系列元素生成哈夫曼编码
template <class T>
std::string HuffmanTree<T>::CreateCompleteCode(std::vector<std::optional<T>> v) const
{
std::string ret = "";
for (auto p : v)
{
ret += CreateCode(p);
}
return ret;
}
//传参为字符串的重载
template <class T>
std::string HuffmanTree<T>::CreateCompleteCode(std::string str)
{
return "";
}
//传参为字符串的重载
template <>
std::string HuffmanTree<char>::CreateCompleteCode(std::string str)
{
std::string ret = "";
for (char c : str)
{
ret += CreateCode(c);
}
return ret;
}
//打印所有元素及对应编码
template <class T>
void HuffmanTree<T>::PrintAllCodes()
{
for (int i = 1; i <= size; i++)
{
std::cout << arr[i].e << " : " << encode_mp[arr[i].e] << std::endl;
}
}
//打印所有元素及对应编码
template <class T>
void HuffmanTree<T>::PrintAllCodes(std::vector<T> &elems)
{
for (auto elem : elems)
{
std::cout << elem << " : " << encode_mp[elem] << std::endl;
}
}
//为一串哈夫曼编码解码
template <class T>
std::string HuffmanTree<T>::Decode(std::string str)
{
std::string ret, buf;
for (auto c : str)
{
if (!decode_mp.count(buf))
buf.push_back(c);
else
{
ret += *decode_mp[buf];
buf = "";
buf.push_back(c);
}
}
ret += *decode_mp[buf];
return ret;
}
template <class T>
HuffmanTree<T>::HuffmanTree(int n, std::vector<T> &elems, std::vector<int> &weights) : size(n), idx(n)
{
arr = new TrNode<T>[1 << n];
for (int i = 1; i <= n; i++)
{
arr[i].e = elems[i - 1];
arr[i].weight = weights[i - 1];
}
CreateHuffmanTree();
}
template <class T>
HuffmanTree<T>::~HuffmanTree()
{
free(arr);
}
#endif