哈夫曼 编码解码

给定字符串, 建造哈夫曼树,并将字符串转为哈夫曼编码解码

1. JS

  1. 字符串
    在这里插入图片描述

  2. 字符权值
    在这里插入图片描述

  3. 哈夫曼树建立
    在这里插入图片描述

  4. 字符所对应的哈夫曼编码
    在这里插入图片描述 5. 字符串编码结果
    在这里插入图片描述

  5. JS源码 :

/**
 * @author 轩 http://wx0725.top
 * 如果a.val存在,定义类属性a.val()返回左右和
 */
HAHA.prototype.val = function() {
	return (this.left.type ? this.left.val() : this.left.value) + (this.right.type ? this.right.val() : this.right.value);
};

/**
 * @param {Object} left
 * @param {Object} right
 * 
 * this.left.val 来判断此前结点是否为树
 * 如果this.left.val没有定义则直接取值
 */
function HAHA(left, right) { // haha类初始化
	this.type = "我是树"; // 用来记录当前的结点是树
	this.left = left; //左子节点
	this.right = right; //右子节点
}
/**
 * @param {Object} list 
 * 创建哈夫曼树
 */
HAHA.create = function(list) {
	while (list.length > 1) {
		list.sort(function(a, b) {
			a = a.type ? a.val() : a.value; // 如果这个结点是树,则取树的左右和, 否则取值
			b = b.type ? b.val() : b.value;
			return a - b; // 从小到大排序
		});
		var item = new HAHA(list.shift(), list.shift()); // “取出” 前两个小值,并且创建新的树
		list.push(item); // 将新的树插入哈夫曼树
	}
	return list[0] // 返回最终的树
}

/**
 * @param {Object} list
 * code_now 记录当前访问的位置编码
 */
var code_now = "";

function Code(list) { // 编码
	if (list.type) { // 如果是树
		if (list.left) { // 有左树
			code_now = code_now + "0";
			Code(list.left);
			code_now = code_now.substr(0, code_now.length - 1); // 删掉递归回来的最后一个编码
		}
		if (list.right) { // 有右树
			code_now = code_now + "1";
			Code(list.right);
			code_now = code_now.substr(0, code_now.length - 1);
		}
	} else { // 如果
		res_code[list.name] = code_now;
	}
}

/**
 * 主函数
 */
main()
var res_code = [];

function main() {
	res_code = []
	var str = "I love Zhoukou Normal University";
	var arr = []; //存储每个字母的出现次数 权值
	for (var i = 0; i < str.length; i++) { // map
		if (arr[str[i]] != undefined) {
			arr[str[i]]++;
		} else {
			res_code[str[i]] = "";
			arr[str[i]] = 1;
		}
	}
	console.log("权值:")
	console.log(arr)
	/**
	 * @param {Object} var a in arr
	 * 将字母对应的值存在数组中
	 */
	var list = [];
	for (var a in arr) {
		list.push({
			name: a,
			value: arr[a],
		});
	}
	var hufftree = HAHA.create(list);
	Code(hufftree);
	console.log("哈夫曼树:")
	console.log(hufftree);
	console.log("哈夫曼树编码结果:")
	console.log(res_code);
}

2. C语言

== 请保存为 .cpp文件运行==

在这里插入图片描述

#include <stdio.h>
#include <malloc.h>
#include <limits.h>
#include <string.h>
typedef struct {
	char value; // 字母
	unsigned int weight; // 权值
} Data;
typedef struct {
	char value; // 字符
	unsigned int weight; // 权值
	unsigned int status; // 状态
	unsigned int parent;
	unsigned int lchild, rchild;
} HTNode;

void SelectTwoMinNode(HTNode *HT,int endIndex,int &s1,int &s2);

void CreateHT(HTNode **HT,Data *pw,int n);

char** ProduceHCFromLeafToRoot(HTNode *HT,int n);

char** ProduceHCFromRootToLeaf(HTNode *HT,int n);

void PrintHTCode(char **HTCode,int n, char str[]);

void CharSetHuffmanDecoding(HTNode *T, int n);

char a[128];



int main() {

	int n, i, j;

	Data *pw = NULL;  //权重数组,数据类型根据需要设置

//1 读入n个权重值,0号数组空间不用
	pw = (Data *)malloc(sizeof(Data) * 300); // 最多 128 个字母
	char str[] = "I love Zhoukou Normal University"; // 进行 Huffman 排序的 String
	unsigned int arr[128] = {0};
	for(i = 0; str[i] != '\0'; i++) { // 统计 各字母出现的次数
		arr[str[i]]++;
	}
	for(i = 0, j = 1; i < 128; i++) { // 0 作为根节点 (j)
		if(arr[i] != 0) {
			pw[j].value = i;
			pw[j].weight = arr[i];
			j += 1;
		}
	}
	n = j - 1;
	HTNode *HT = NULL;
	CreateHT( &HT, pw, n);
	char **HTCode = ProduceHCFromRootToLeaf(HT,n);  // 从根到叶子 Huffman 树
	PrintHTCode(HTCode,n, str); // 结果
	CharSetHuffmanDecoding(HT, n);
	return 0;
}



// 函数功能:构建Huffman树 ,n代表权重个数,且0号空间没有用

void CreateHT(HTNode **HT,Data *pw,int n) {
	int s1,s2;
	int m = 2 * n - 1;
	if(n <= 1) return;
	*HT = (HTNode *)malloc(( m + 1 )*sizeof(HTNode));
	for(int i = 1; i <= m; i++) { //1 初始化Huffman树
		(*HT)[i].parent = 0;
		(*HT)[i].lchild = 0;
		(*HT)[i].rchild = 0;
		if(i <= n) {
			(*HT)[i].weight = pw[i].weight;
			(*HT)[i].value = pw[i].value;
		} else {
			(*HT)[i].weight = 0;
			(*HT)[i].value = '@';
		}
	}

	for(int i = n + 1; i <= m; i++) { //2 构建Huffman树

		SelectTwoMinNode(*HT,i-1,s1,s2);



		(*HT)[s1].parent = i;

		(*HT)[s2].parent = i;

		(*HT)[i].lchild = s1;

		(*HT)[i].rchild = s2;

		(*HT)[i].weight = (*HT)[s1].weight + (*HT)[s2].weight;

	}

}



// 功能:在HT中,从位置0~endIndex选择权重值最小的两个结点

void SelectTwoMinNode(HTNode *HT,int endIndex,int &s1,int &s2) {
//1初始化s1和s2
	HT[0].weight = UINT_MAX;
	s1 = s2 = 0;   //假设最小和次小权值位序都是0
	for(int i = 1; i <= endIndex; i++) {
//如果第i个结点被选择过,或者其权重值比次小权值的还大
		if(HT[i].parent != 0 || HT[i].weight >= HT[s2].weight) {
			continue;
		}
		if(HT[i].weight >= HT[s1].weight) {
			s2 = i;
		} else {
			s2 = s1;
			s1 = i;
		}
	}
}

char** ProduceHCFromRootToLeaf(HTNode *HT,int n) {
	int codelen = 0;  //
	int p = 2*n-1;  //设置p为根节点位序
	char *pcode = (char *)malloc(n*sizeof(char));  //编码临时周转空间
	char **HTCode = (char **)malloc((n+1)*sizeof(char *));//Huffuman编码结果空间
	for(int i = 1; i <= p; i++) {
		HT[i].status = 0; // 0 表示没有遍历   1 表示遍历过左孩子   2表示遍历过右
	}
	while( p != 0) {
		if(HT[p].status == 0) { //向左遍历
			HT[p].status = 1;  //记录向左走过
			if(HT[p].lchild != 0) { // 有左孩子
				p = HT[p].lchild;
				pcode[codelen] = '0';
				codelen = codelen + 1;
			} else if(HT[p].rchild == 0) { // 没有左孩子  并且没有右孩子
				HTCode[p] = (char *)malloc((codelen+1)*sizeof(char));
				pcode[codelen] = '\0';
				a[p] = HT[p].value;
				strcpy(HTCode[p],pcode);
			}
		} else if(HT[p].status == 1) { //向右
			HT[p].status = 2;
			if(HT[p].rchild != 0) { // 有右孩子
				p = HT[p].rchild;
				pcode[codelen] = '1';
				codelen = codelen + 1;
			}
		} else { // 没有孩子
			HT[p].status = 0;
			p = HT[p].parent;
			codelen = codelen -1;
		}
	}
	free(pcode);
	return HTCode;
}


char encode[1000]="";
void PrintHTCode(char **HTCode, int n, char str[]) {// 打印 Huffman 编码
	
	puts("哈弗曼编码:");
	for(int i = 1; i <= n; i++)
		printf("%c %s\n",a[i], HTCode[i]);
	puts("\n编码:");
	for(int i = 0; i < str[i]!='\0'; i++) {
		for(int ii = 1; ii <= n; ii++) {
			if(a[ii] == str[i]) {
				strcat(encode, HTCode[ii]);
				break;
			}
		}
	}
	printf("%s\n", encode);
}


void CharSetHuffmanDecoding(HTNode *T, int n) {
	puts("\n解码:");
	int p=2*n-1;      //从根结点开始
	int i=0;
	//当要解码的字符串没有结束时
	while(encode[i]!='\0') {
		//当还没有到达哈夫曼树的叶子并且要解码的字符串没有结束时
		while((T[p].lchild!=0 && T[p].rchild != 0) && encode[i] != '\0') {
			if(encode[i] == '0') {
				//如果是0,则叶子在左子树
				p=T[p].lchild;
			} else {
				//如果是1,则叶子在左子树
				p=T[p].rchild;
			}
			i++;
		}
		//如果到达哈夫曼树的叶子时
		if(T[p].lchild == 0 && T[p].rchild == 0) {
			printf("%c", T[p].value);
			p = 2*n-1;
		} else {  //如果编号为p的结点不是叶子,那么编码有错
			printf("\n解码出错! \n");
			return;
		}
	}
	printf("\n");
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流星蝴蝶没有剑

篮球弹弹弹

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值