使用堆优化哈夫曼编码

之前使用二叉树的创建方法创建了一棵哈夫曼树,实现了哈夫曼编码,如今用堆将哈夫曼编码优化一下!

我们优化项目主要在于哈夫曼编码中,寻找两个频次最低的字符的过程,之前使用的是遍历方法,效率较低,现在使用小顶堆的维护性质便可以轻松解决效率低下的问题!

时间复杂度: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;
}

  • 15
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值