给定字符串, 建造哈夫曼树,并将字符串转为哈夫曼编码解码
1. JS
-
字符串
-
字符权值
-
哈夫曼树建立
-
字符所对应的哈夫曼编码
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");
}