给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。
这里使用STL容器适配器priority_queue实现哈夫曼树.
提示:
typedef struct Haffm {
int priority;//有字符的频率(优先级)
char word;//能存储字符
Haffm* lchild;
Haffm* rchild;//有左子结点和右子结点
}Haffmtree;
使用优先队列建树过程:
Haffmtree* buildHaffmtree(priority_queue<Haffmtree*, vector<Haffmtree*>, myCompare> &haffm) {
if (haffm.empty()) {
exit(1);
}
do {
Haffmtree* first = haffm.top();
haffm.pop();
if (haffm.empty()) {
return first;
}
Haffmtree* root=new Haffmtree;
root->lchild = first;
root->rchild = haffm.top();
root->word = '-';
root->priority = root->lchild->priority + root->rchild->priority;
haffm.pop();
haffm.push(root);
} while (!haffm.empty());
}
优先队列的模板类型,第一个参数是存储对象的类型,第二个参数是存储元素的底层容器,第三个参数是函数对象,是比较的方法(依据).后两个参数有默认值,底层容器默认为vector,比较方法默认为less<>(),这是一个仿函数,后文会详细讲到.本文将自定义比较方法.
特别说明:如果需要指定第三个参数,那么也需要指定第二个参数.
template <typename T, typename Container=std::vector<T>, typename Compare=std::less<T>> class priority_queue
在STL提供的一些适配器和排序方法中,常用到比较函数,有时默认的不能满足我们的要求,这时就需要我们自定义.一般有以下几种方法:
template<>
struct less<void>
{ // transparent functor for operator<
typedef int is_transparent;
template<class _Ty1,
class _Ty2>
constexpr auto operator()(_Ty1&& _Left, _Ty2&& _Right) const
-> decltype(static_cast<_Ty1&&>(_Left)
< static_cast<_Ty2&&>(_Right))
{ // transparently apply operator< to operands
return (static_cast<_Ty1&&>(_Left)
< static_cast<_Ty2&&>(_Right));
}
};
这是库中针对基础数据类型写的默认的比较函数,大的排在前面,如果我们把这里的"<"重载,变成自己想要的效果,比如以传进来的自定义的结构体的某个成员的大小为依据排序,那么就能用它默认的比较方法达成对特定的元素比较的效果.
typedef struct Haffm {
int priority;
char word;
Haffm* lchild;
Haffm* rchild;
bool operator<(Haffm &a) {
return priority > a.priority;//小的排在前面
}
}Haffmtree;
重写如上,但是本篇代码是不能用这个方法的,因为适配器里装的是Haffm*
类型的数据,而我重载的其实是Haffm
类型数据的比较方法.不过读者可以类比这个例子写自己的重载函数.
2. 函数指针
这个就很简单,写一个自己的比较函数,然后把函数名(也就是函数指针)传入参数.改写如下,这里也不能使用这个方法,因为参数明确指出要函数对象,不过读者可以类比写自己的比较函数.
bool myCompare(Haffmtree* a, Haffmtree* b) {
return a->priority > b->priority;//小的排在前面
}
仿函数(functor),就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了。
仿函数可以通过结构体实现(事实上结构体也是类的一种),也可以通过类实现,传参的时候把结构体名/类名当做函数名传入就可以了.在这里我两种代码都会给出,读者可以类比写自己的仿函数.
struct myCompare {//结构体实现仿函数
bool operator()(Haffmtree* &a, Haffmtree* &b) {
return a->priority > b->priority;
}
};
class mycompare {
public:
bool operator()(Haffmtree* &a, Haffmtree* &b) {
return a->priority > b->priority;
}
};
#include
#include
#include
using namespace std;
typedef struct Haffm {
int priority;
char word;
Haffm* lchild;
Haffm* rchild;
}Haffmtree;
struct myCompare {
bool operator()(Haffmtree* &a, Haffmtree* &b){
return a->priority > b->priority;
}
};
Haffmtree* buildHaffmtree(priority_queue<Haffmtree*, vector<Haffmtree*>, myCompare> &haffm) {
if (haffm.empty()) {
exit(1);
}
do {
Haffmtree* first = haffm.top();
haffm.pop();
if (haffm.empty()) {
return first;
}
Haffmtree* root=new Haffmtree;
root->lchild = first;
root->rchild = haffm.top();
root->word = '-';
root->priority = root->lchild->priority + root->rchild->priority;
haffm.pop();
haffm.push(root);
} while (!haffm.empty());
}
void preorder(Haffmtree* &root) {//前序遍历打印结果
if (!root) {
return;
}
cout << root->word << endl;
preorder(root->lchild);
preorder(root->rchild);
}
int main(void) {
priority_queue<Haffmtree*, vector<Haffmtree*>, myCompare> haffm;
int n;
cout << "请输入编码的字符的个数:";
cin >> n;
while (n--) {
Haffmtree* node = new Haffmtree;
cout << "请输入字符:";
cin >> node->word;
cout << "请输入出现的频率:";
cin >> node->priority;
node->lchild = node->rchild = nullptr;
haffm.push(node);
}
Haffmtree* root = buildHaffmtree(haffm);
preorder(root);
system("pause");
return 0;
}