Huffuman编码思想:根据字符出现的概率大小进行编码,出现概率高的字符使用较短的编码,出现概率低的字符使用较长的编码。
附上一个图个一个微博链接,可以自己感受一下。哈夫曼树以及哈夫曼编码的构造步骤_LDF-Dicky的博客-CSDN博客_哈夫曼树的构造
代码实现:这部分代码粗看不难,但是细细分析之中的实现还是挺费脑的,尤其是中间在做递归之前传入的参数的结构,可以调试自己分析一下。
// Huffuman.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <queue>
#include <vector>
#include <string>
using namespace std;
//每一个符号定义为一个结构体,包括字符和出现频次
typedef struct
{
unsigned char character; //定义字符是谁
unsigned int frequency; //定义字符出现的频率
} CharNode;
static bool open_input_file(ifstream &input, const char *inputFileName)
{
input.open(inputFileName);
if (!input.is_open())
{
return false;
}
return true;
}
//定义一个二叉树结点
struct MinHeapNode
{
char data; //值
unsigned int freq; //频次
MinHeapNode *left, *right; //定义两个子节点
MinHeapNode(char data, unsigned freq) //定义结构体的构造函数,结构体和类的区别(可自行百度)
{
left = right = NULL;
this->data = data;
this->freq = freq;
}
};
typedef struct MinHeapNode MinHeapNode;
//重写仿函数,实现降序,运算符重载也可以实现,具体见:https://blog.csdn.net/weixin_36888577/article/details/79937886
struct compare
{
bool operator()(MinHeapNode* l, MinHeapNode *r)
{
return (l->freq > r->freq);
}
};
static void get_huffman_code(MinHeapNode *root, string code)
{ //第一个参数表示根节点,第二次参数用于输出一个字符
if (!root) //如果根节点为空
{
return;
}
if (root->data != -1) //下面合并生成的新字符都赋值成了-1,如果不是-1,到了最下面的子节点
{
cout << root->data << " : " << code << endl;;
}
get_huffman_code(root->left, code + "0"); //如果到了根节点,在左子树节点上加0,在右子树节点加1
get_huffman_code(root->right, code + "1");
}
int _tmain(int argc, _TCHAR* argv[])
{
ifstream inputFile; //构建文件输入流对象
if (!open_input_file(inputFile, "input.txt")) //打开input.txt文件
{
cout << "Error: opening input file failed!" << endl;
return -1;
}
char buf = inputFile.get(); //从文件流中读取一个字符放到buf中
CharNode nodeArr[256] = { { 0, 0 } }; //一个字符,占据一个字节,有256中可能2^8 = 256,将里面元素全部初始化为0
while (inputFile.good()) // 表示文件正常,没有读写错误,也不是坏的,也没有结束
{
cout << buf;
nodeArr[buf].character = buf; //同样的结果会重复赋值,不造成影响
nodeArr[buf].frequency++; //频率++,在全部遍历完之后就可以知道全部字符出现多少此
buf = inputFile.get();
}
cout << endl;
priority_queue<MinHeapNode*, vector<MinHeapNode*>, compare> minHeap;
for (int idx = 0; idx < 256; idx++)
{
if (nodeArr[idx].frequency > 0)
{ //输出字符和次数
cout << "Node " << idx << ": [" << nodeArr[idx].character << ", " << nodeArr[idx].frequency << "]" << endl;
minHeap.push(new MinHeapNode(nodeArr[idx].character, nodeArr[idx].frequency));//使用优先级队列实现排序功能
}
}
MinHeapNode *leftNode = NULL, *rightNode = NULL, *topNode = NULL;
while (minHeap.size() != 1) //只剩下根节点的时候停止运行
{
leftNode = minHeap.top(); //得到优先级最小的那个元素
minHeap.pop(); //弹出最小元素
rightNode = minHeap.top(); //再拿到一个最小元素
minHeap.pop();
topNode = new MinHeapNode(-1, leftNode->freq + rightNode->freq); //两个最小的节点合并成一个节点,-1可以理解成一个虚值,不在文章里面的一个字符
topNode->left = leftNode;
topNode->right = rightNode;
minHeap.push(topNode); //将刚刚构造好的新节点放进minHeap里面重新进行排序
}
get_huffman_code(topNode, ""); //出循环之后,topNode表示的就是根节点
inputFile.close();
return 0;
}
队列优先级实现:
#include<iostream>
#include <queue>
using namespace std;
//定义:priority_queue<Type, Container, Functional>
/*
Type 就是数据类型,Container 就是容器类型(Container必须是用数组实现的容器,
比如vector,deque等等,但不能用 list。STL里面默认用的是vector),
Functional 就是比较的方式,当需要用自定义的数据类型时才需要传入这三个参数,
使用基本数据类型时,只需要传入数据类型,默认是大顶堆
*/
int main()
{
//对于基础类型 默认是大顶堆,降序
priority_queue<int> a;
//等同于 priority_queue<int, vector<int>, less<int> > a;
priority_queue<int, vector<int>, greater<int> > c; //这样就是小顶堆,升序
priority_queue<string> b;
for (int i = 0; i < 5; i++)
{
a.push(i);
c.push(i);
}
while (!a.empty())
{
cout << a.top() << ' ';
a.pop();
}
cout << endl;
while (!c.empty())
{
cout << c.top() << ' ';
c.pop();
}
cout << endl;
//字符,按照字母的先后顺序
b.push("abc");
b.push("abcd");
b.push("cbd");
while (!b.empty())
{
cout << b.top() << ' ';
b.pop();
}
cout << endl;
return 0;
}