格式
输入
一串小写字母组成的字符串(不超过1000000)。
输出
输出这个字符串通过Huffman编码后的长度。
样例
输入
abcdabcaba
输出
19
#include<iostream>
#include<string>
using namespace std;
struct HuffmanNode{
//定义霍夫曼节点
int element;//权重
HuffmanNode *leftChild,*rightChild;//左右孩子
int line;//路线长度
HuffmanNode(){
}
};
template <class T>//链表结构
struct chainNode {
T element;
chainNode<T> *next;
chainNode() {
}
chainNode(const T& element)
{
this->element = element;}
chainNode(const T& element, chainNode<T>* next)
{
this->element = element;
this->next = next;}
};
template<class T>
class linkedQueue{
public:
linkedQueue(int initialCapacity = 10):queueFront(NULL),queueSize(0){
}//初始化队列
~linkedQueue();//析构函数
bool empty()const {
return queueSize == 0;}
int size()const{
return queueSize;}
T& front(){
if (queueSize == 0)
exit(1);
return queueFront->element;
}
T& back(){
if (queueSize == 0)
exit(1);
return queueBack->element;
}
void pop();//删除队首
void push(const T&);//插入到队尾
private:
chainNode<T>* queueFront;//头指针
chainNode<T>* queueBack;//尾指针
int queueSize;
};
//析构函数
template<class T>
linkedQueue<T>::~linkedQueue(){
while (queueFront != NULL){
chainNode<T>* nextNode = queueFront->next;
delete queueFront;
queueFront = nextNode;
}
}
//删除队首
template<class T>
void linkedQueue<T>::pop(){
if (queueFront == NULL)
exit(1);
chainNode<T>* nextNode = queueFront->next;
delete queueFront;
queueFront = nextNode;
queueSize--;
}
template<class T>
void linkedQueue<T>::push(const T& theElement){
chainNode<T>* newNode = new chainNode<T>(theElement, NULL);
if (queueSize == 0)
queueFront = newNode;
else
queueBack->next = newNode;
queueBack = newNode;
queueSize++;
}
//最小堆类
class minHeap{
public:
minHeap(){
length = 0;p=new HuffmanNode*[27]; }//定义一个HUffmanNide类型的数组 //27是因为第0位不用
~minHeap() {
delete[]p; }
void initialize(HuffmanNode* *a/*指针数组*/, int n);
void insert(HuffmanNode *num);
void pop();
HuffmanNode* top() {
return p[1];}
private:
int length;
HuffmanNode **p;
};
void minHeap::initialize(HuffmanNode **a, int n){
//初始化
length = n;
for (int i = 0; i < n; i++)
p[i + 1] = a[i];
//堆化
for (int root = length / 2; root >= 1; root--){
HuffmanNode* rootelement = p[root];
int child = 2 * root;
//为element寻找位置
while (child <= length){
//heap[child]应该为两兄弟的较小者
if (child<length&&p[child]->element>p[child + 1]->element){
child++;
}
//可以把元素rootelement放在heap[child/2]吗
if (rootelement->element <= p[child]->element)
break;//可以
else{
//不可以
p[child / 2] = p[child];//把孩子向上移
child = child * 2;//移到下一层
}
}
p[child / 2] = rootelement;
}
}
void minHeap::insert(HuffmanNode* num){
//插入函数
int current = ++length;//元素将要插入的位置
while (current > 1 && p[current / 2]->element > num->element){
//不能把元素element插入heap[current]
p[current] = p[current / 2];
current = current / 2;
}
p[current] = num;
}
void minHeap::pop(){
//删除函数
HuffmanNode* lastelement = p[length];
int root = 1;
int child = 2;
length--;
while (child <= length){
if (child<length&&p[child]->element>p[child + 1]->element){
//应为较小的孩子
child++;
}
//可以把元素lastelement放在heap[root]吗
if (lastelement->element <= p[child]->element)
break;//可以
p[root] = p[child];
root = child;
child = child * 2;
}
p[root] = lastelement;
}
class Huffmantree{
public:
HuffmanNode *root;//定义根
Huffmantree() {
}//构造函数
~Huffmantree();//析构函数
void maketree(int *a,int n);//n为字符串的size
void length();//霍夫曼编码长度
private:
int size; //
};
//析构函数
Huffmantree::~Huffmantree(){
linkedQueue<HuffmanNode*> q;//HuffmanNode结构的队列
q.push(root);
for (int i = 0; i < size; i++){
HuffmanNode* currentNode = q.front();
q.pop();
if (currentNode->leftChild != NULL){
q.push(currentNode->leftChild);
}
if (currentNode->rightChild != NULL){
q.push(currentNode->rightChild);
}
delete currentNode;
}
}
//建立一棵霍夫曼树
void Huffmantree::maketree(int *a, int n){
HuffmanNode **hufnode = new HuffmanNode*[n];
for (int i = 0; i < n; i++){
//置空
hufnode[i] = new HuffmanNode();
hufnode[i]->element = a[i];//赋予权值
hufnode[i]->leftChild = hufnode[i]->rightChild = NULL;
}
minHeap h;
h.initialize(hufnode, n);
HuffmanNode *w, *x, *y;
// 将配对堆中最小的两个节点取出,进行合并,将合并后的节点重新加入配对堆
for (int i = 1; i < n; i++){
//建立一棵霍夫曼树
x = h.top(); h.pop();//顶端即为最小,拿出后删除
y = h.top(); h.pop();
w = new HuffmanNode();//合并最小的两个
w->leftChild = x;
w->rightChild = y;
w->element = x->element + y->element;//新权值
x = NULL;y = NULL;//删除
h.insert(w);//重新加入,不过这时只算做一个节点
}
size = n;//代表有几个不同的字母
root = h.top();
h.pop();
}
//计算霍夫曼编码的长度
void Huffmantree::length(){
int result = 0;
linkedQueue<HuffmanNode*> que;
HuffmanNode *currentNode;
que.push(root);//将堆首压入
root->line = 0;//初始化为0,只要有向左 或者向右 则路线加一
while (!que.empty()){
currentNode = que.front();
que.pop();
//类似前序遍历的思路
if (currentNode->leftChild != NULL){
que.push(currentNode->leftChild);
currentNode->leftChild->line = currentNode->line + 1;
}
if (currentNode->rightChild != NULL){
que.push(currentNode->rightChild);
currentNode->rightChild->line = currentNode->line + 1;
}
if (currentNode->leftChild == NULL && currentNode->rightChild == NULL){
result = result + currentNode->line*currentNode->element;//为当个字母的路线结束标志,即为叶节点了 代表这个字母总的占用的长度
}
}
cout << result;// 所有的长度综合
}
int main(){
string str;int index = 0,n= 0;
cin>>str;
int *a = new int[26];//统计各个字母出现频率
for (int i = 0; i < 26; i++)
a[i] = 0;
for (int j = 0; j < str.size(); j++){
a[(int)str[j] - 97]++;
if (a[(int)str[j] - 97] == 1)
n++;//每个字母只算一次,计算不为0 的字符的个数
}
int *b = new int[n];
for (int k = 0;k < 26; k++){
if (a[k] != 0){
b[index]=a[k];//只包含不为0次数的字母
index++;
}
}
Huffmantree Huff;
Huff.maketree(b,n);
Huff.length();
return 0;
}