数据结构书上的HuffmanTree的实现算法是O(n ^ 2)的复杂度,比较好理解,也好实现,所以就不赘述了,在这里说一下O(nlogn)的复杂度的HuffmanTree的实现方法:构造一个二叉堆,维护小顶堆。
代码:
#include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;
typedef struct hNode
{
int weight;
int lc, rc;
} *HuffmanTree;
HuffmanTree CreateHuffmanTree(const int w[], int n); //创建一棵HuffmanTree
void BuildHeap(HuffmanTree t, int n); //构造一个二叉堆:小顶堆
void PercDown(HuffmanTree t, int pos, int n); //构造二叉堆的功能子函数
void DeleteMin(HuffmanTree t, int len); //删除二叉堆的根,并通过上移使得新得到的序列仍为二叉堆
void insertHfNode(HuffmanTree t, int len, struct hNode x); //把x插入到原长度为len - 1的二叉堆
void Preorder(HuffmanTree t, int p, unsigned long long &sum); //先序遍历HuffmanTree,累计非叶子节点的值
int main()
{
int n;
scanf("%d", &n);
int *a = new int[n];
for (int i = 0; i < n; ++i)
scanf("%d", &a[i]);
HuffmanTree hT = CreateHuffmanTree(a, n);
unsigned long long sum = 0;
Preorder(hT, 1, sum);
if (n == 1) sum = a[0];
cout << sum << endl;
return 0;
}
HuffmanTree CreateHuffmanTree(const int w[], int n)
{
HuffmanTree hT = new struct hNode[2 * n]; //第一个节点不用
for (int i = 0; i < n; ++i)
{
hT[i + 1].weight = w[i];
hT[i + 1].lc = hT[i + 1].rc = 0;
}
BuildHeap(hT, n);
struct hNode add;
int left = n;
int right = n;
while (left > 1) //此处通过减小和增大left的值来改变二叉堆的大小
{
hT[++right] = hT[1];
add.weight = hT[1].weight;
add.lc = right; //存储左孩子下标
DeleteMin(hT, left--);
hT[left + 1] = hT[1];
add.weight += hT[1].weight;
add.rc = left + 1; //存储右孩子下标
DeleteMin(hT, left);
insertHfNode(hT, left, add);
}
return hT;
}
void BuildHeap(HuffmanTree t, int len)
{
for (int i = len / 2; i > 0; --i)
{
PercDown(t, i, len);
}
}
void PercDown(HuffmanTree t, int pos, int len)
{
int child;
struct hNode min = t[pos];
while (pos * 2 <= len)
{
child = pos * 2;
if (child != len && t[child + 1].weight < t[child].weight)
child++;
if (min.weight > t[child].weight)
t[pos] = t[child];
else
break;
pos = child;
}
t[pos] = min;
}
void DeleteMin(HuffmanTree t, int len)
{
struct hNode last = t[len--]; //二叉堆的最后一个元素
int child, pos = 1;
while (pos * 2 <= len) //把二叉堆的某些元素往前移,使得新得到的序列仍为二叉堆
{
child = pos * 2;
if (child != len && t[child + 1].weight < t[child].weight) //若i有右儿子,且右儿子小于左儿子,c指向右儿子
child++;
if (last.weight > t[child].weight) //若i的小儿子小于二叉堆的最后一个元素,把其移到i的位置
t[pos] = t[child];
else
break;
}
t[pos] = last; //把二叉堆的最后一个元素放到适当的空位,此时得到的序列仍为二叉堆
}
void insertHfNode(HuffmanTree t, int len, struct hNode x)
{
int i;
for (i = len; i / 2 > 0 && t[i / 2].weight > x.weight; i /= 2)
t[i] = t[i / 2];
t[i] = x;
}
void Preorder(HuffmanTree t, int p, unsigned long long & sum)
{
if (t[p].lc > 0)
{
sum += t[p].weight;
Preorder(t, t[p].lc, sum); //遍历左子树
Preorder(t, t[p].rc, sum); //遍历右子树
}
}