之前使用二叉树的创建方法创建了一棵哈夫曼树,实现了哈夫曼编码,如今用堆将哈夫曼编码优化一下!
我们优化项目主要在于哈夫曼编码中,寻找两个频次最低的字符的过程,之前使用的是遍历方法,效率较低,现在使用小顶堆的维护性质便可以轻松解决效率低下的问题!
时间复杂度:O(nlogn)
废话不多说!代码如下:
下面是建堆代码:
typedef struct Heap {
Node **__data, **data; //__data是实际的存储区首地址,data是偏移后存储器的逻辑首地址
int n, size;
} Heap;
Heap *getNewHeap(int size) { //堆的初始化
Heap *h = new Heap;
h->__data = new Node*[size];
h->data = h->__data - 1;
h->n = 0;
h->size = size;
return h;
}
void clearHeap(Heap *h) { //堆的销毁
if (h == NULL) return;
delete h->__data;
delete []h;
return;
}
bool fullHeap(Heap *h) { //判断堆是否满了
return h->n == h->size;
}
bool emptyHeap(Heap *h) {
return h->n == 0;
}
Node *top(Heap *h) {
if (emptyHeap(h)) return NULL;
return h->data[1];
}
void up_maintain(Heap *h, int i) {
while (i > 1 && h->data[i]->freq < h->data[i/2]->freq){//当前节点不是顶节点,且当前节点值小于父节点时候,需要向上调整
swap(h->data[i],h->data[i / 2]);
i = i / 2;
}
return;
}
void down_maintain(Heap *h,int i){
while (i * 2 <= h->n){//当前节点还存在子节点,则可以进行向下调整
int ind = i, l = i * 2, r = i * 2 + 1;
if (h->data[l]->freq < h->data[ind]->freq) ind = l;
if (r <= h->n&&h->data[r]->freq < h->data[ind]->freq) ind = r;
if (ind == i) return;
swap(h->data[i],h->data[ind]);
i = ind;//重复地向下调整
}
return;
}
int pushHeap(Heap *h, Node *n) {
if (fullHeap(h)) return 0; //堆满则压入不成功
h->n += 1;
h->data[h->n] = n;//将新的节点指针压入堆底
up_maintain(h, h->n); //第h->n个元素执行向上调整
return 1;
}
int popHeap(Heap *h) {
if (emptyHeap(h)) return 0; //堆空 弹出不成功
h->data[1] = h->data[h->n];//将堆底元素覆盖堆顶元素
h->n -= 1;
down_maintain(h, 1); //向下调整
这里建堆使用了一个小技巧,__data是实际存储区,data是向后偏移一位的逻辑存储区,这样就能够实现访问data[1]从而访问__data[0]啦。
下面是完整优化后代码:
/*小顶堆优化哈夫曼编码中的挑取最小频次步骤*/
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#define swap(a,b){\
__typeof(a) __c = a;\
a = b, b = __c;\
}
using namespace std;
typedef struct Node {
char ch;//字符
int freq;//频次
struct Node *lchild, *rchild;
} Node;
/*--------------------------------------小顶堆-------------------------------------------*/
typedef struct Heap {
Node **__data, **data; //__data是实际的存储区首地址,data是偏移后存储器的逻辑首地址
int n, size;
} Heap;
Heap *getNewHeap(int size) { //堆的初始化
Heap *h = new Heap;
h->__data = new Node*[size];
h->data = h->__data - 1;
h->n = 0;
h->size = size;
return h;
}
void clearHeap(Heap *h) { //堆的销毁
if (h == NULL) return;
delete h->__data;
delete []h;
return;
}
bool fullHeap(Heap *h) { //判断堆是否满了
return h->n == h->size;
}
bool emptyHeap(Heap *h) {
return h->n == 0;
}
Node *top(Heap *h) {
if (emptyHeap(h)) return NULL;
return h->data[1];
}
void up_maintain(Heap *h, int i) {
while (i > 1 && h->data[i]->freq < h->data[i/2]->freq){//当前节点不是顶节点,且当前节点值小于父节点时候,需要向上调整
swap(h->data[i],h->data[i / 2]);
i = i / 2;
}
return;
}
void down_maintain(Heap *h,int i){
while (i * 2 <= h->n){//当前节点还存在子节点,则可以进行向下调整
int ind = i, l = i * 2, r = i * 2 + 1;
if (h->data[l]->freq < h->data[ind]->freq) ind = l;
if (r <= h->n&&h->data[r]->freq < h->data[ind]->freq) ind = r;
if (ind == i) return;
swap(h->data[i],h->data[ind]);
i = ind;//重复地向下调整
}
return;
}
int pushHeap(Heap *h, Node *n) {
if (fullHeap(h)) return 0; //堆满则压入不成功
h->n += 1;
h->data[h->n] = n;//将新的节点指针压入堆底
up_maintain(h, h->n); //第h->n个元素执行向上调整
return 1;
}
int popHeap(Heap *h) {
if (emptyHeap(h)) return 0; //堆空 弹出不成功
h->data[1] = h->data[h->n];//将堆底元素覆盖堆顶元素
h->n -= 1;
down_maintain(h, 1); //向下调整
}
/*-----------------------------------------------------------------------------------*/
Node *getNewNode(int freq, char ch) {
Node *p = new Node;
p->ch = ch;
p->freq = freq;
p->lchild = p->rchild = NULL;
return p;
}
void clear(Node *root) {
if (root == NULL) return;
clear(root->lchild);
clear(root->rchild);
delete root;
return;
}
int find_min_node(Node **node_arr, int n) {
int ind = 0;
for (int i = 1; i <= n; i++) {
if (node_arr[ind]->freq > node_arr[i]->freq) ind = i;
}
return ind;
}
void swap_node(Node **node_arr, int ind, int pos) {
Node *temp = node_arr[ind];
node_arr[ind] = node_arr[pos];
node_arr[pos] = temp;
return;
}
Node *buildHaffmanTree(Node **node_arr, int n) {
Heap *h = getNewHeap(n);
for(int i = 0; i < n; i++){
pushHeap(h, node_arr[i]);
}
for (int i = 1; i < n; i++) {
Node *node1 = top(h);
popHeap(h);
Node *node2 = top(h);
popHeap(h);//找到两个freq最小的节点
Node *node3 = getNewNode(node1->freq + node2->freq, 0);
node3->lchild = node1;
node3->rchild = node2;
pushHeap(h,node3);
}
Node *ret = top(h);
clearHeap(h);
return ret;
}
void extractHaffmanCode(Node *root, char buff[], int k) {
buff[k] = 0;
if (root->lchild == NULL && root->rchild == NULL) { //找到叶子节点
printf("%c:%s\n", root->ch, buff);
return;
}
//如果不是叶子节点
buff[k] = '0';
extractHaffmanCode(root->lchild, buff, k + 1);
buff[k] = '1';
extractHaffmanCode(root ->rchild, buff, k + 1);
return;
}
int main() {
char s[10];
int n, freq;
scanf("%d", &n);
Node **node_arr = new Node*[n];
for (int i = 0; i < n; i++) {
scanf("%s%d", s, &freq);
node_arr[i] = getNewNode(freq, s[0]);
}
Node *root = buildHaffmanTree(node_arr, n);
char buff[1000];
extractHaffmanCode(root, buff, 0);
clear(root);
return 0;
}