Huffman树与编码

带权路径最小的二叉树称为最优二叉树或Huffman(哈夫曼树)。

Huffman树的构造

将节点的权值存入数组中,由数组开始构造Huffman树。初始化指针数组,指针指向含有权值的孤立节点。

b = malloc(n*sizeof(BTreeNode));
for (i = 0; i < n; i++)     {
    b[i] = malloc(sizeof(BTreeNode));
    b[i]->data  = a[i];
    b[i]->left  = NULL;
    b[i]->right = NULL;
}

数组b中的指针可以理解为二叉树的根指针。

进行n - 1次循环建立Huffman树

选择b中根节点权值最小的两棵二叉树作为左右子树组成新的二叉树,新二叉树的根节点权值为两颗二叉树根节点权值的和。

将新二叉树添加到b中,并从b中删除原来的两棵二叉树。当b中只有一棵树时终止循环。

int k1 = -1, k2;
for (j = 0; j < n; j++)
//让k1初始指向森林中第一棵树,k2指向第二棵
{
    if (b[j] != NULL && k1 == -1)
    {
        k1 = j;
        continue;
    }
    if (b[j] != NULL)
    {
        k2 = j;
         break;
    }
}
for (j = k2; j < n; j++)
//从当前森林中求出最小权值树和次最小权值树
{
    if (b[j] != NULL)
    {
    if (b[j]->data < b[k1]->data)
    {
        k2 = k1;
        k1 = j;
    }
    else if (b[j]->data < b[k2]->data)
        k2 = j;
    }
}
//由最小权值树和次最小权值树建立一棵新树,q指向树根结点
q = malloc(sizeof(BTreeNode));
q->data = b[k1]->data + b[k2]->data;
q->left = b[k1];
q->right = b[k2];

b[k1] = q;//将指向新树的指针赋给b指针数组中k1位置
b[k2] = NULL;//k2位置为空

Huffman编码与解码

首先给出求带权路径的递归实现:

double WeightPathLength(BTreeNode* FBT, int len) { //len = 0
    if (FBT == NULL) {//空树返回0
        return 0;
    }
    else
    {
        if (FBT->left == NULL && FBT->right == NULL)//访问到叶子结点
            return FBT->data * len;
        else //访问到非叶子结点,进行递归调用,返回左右子树的带权路径长度之和,len递增
            return WeightPathLength(FBT->left,len+1)+WeightPathLength(FBT->right,len+1);
    }
}

上述算法实际上通过双递归遍历了Huffman树。

改进上述算法得到求哈夫曼编码的实现:

static int index = 0;
char *c;
void HuffManCoding(FILE *fp, BTreeNode* FBT, int len)//len初始值为0
{
    static int a[10];//定义静态数组a,保存每个叶子的编码,数组长度至少是树深度减一
    if (FBT != NULL)//访问到叶子结点时输出其保存在数组a中的0和1序列编码
    {
        if (FBT->left == NULL && FBT->right == NULL)
        {
            int i;
            fprintf(fp,"%c %d:",c[index++],FBT->data);
            for (i = 0; i < len; i++)
                fprintf(fp,"%d", a[i]);
           fprintf(fp,"\n");
        }
        else//访问到非叶子结点时分别向左右子树递归调用,并把分支上的0、1编码保存到数组a
        {   //的对应元素中,向下深入一层时len值增1
            a[len] = 0;
            HuffManCoding(fp, FBT->left, len + 1);
            a[len] = 1;
            HuffManCoding(fp, FBT->right, len + 1);
        }
    }
}

节点的Huffman编码由它在Huffman树中的位置决定。从根节点到任意节点有且仅有一条路径,且路径可以唯一确定节点。因此规定从左子结点经过编码为0,从右子结点经过编码为1,路径序列作为编码。

由Huffman树和Huffman编码的性质可知,Huffman编码是一种不等长编码。在构造过程中,两个权值较小的节点生成一棵新的二叉树,根节点的权值为左右子节点的和,并不实际代表字符。也就是说,较短的编码不可能是较长编码的前缀。

Huffman树从叶子到根构造,靠近根的字符节点权值与几个靠近叶子的节点权值和相近,故而靠近根的字符节点权值较高即编码较短。

解码过程可以由字符串匹配来完成:

//Decoding
for(i = 0; code[i]; i++) {
    for (j = 0; j < n; j++) {
        t = 1;
        for (k = 0; coding[j][k]; k++) {
            if (code[i + k] != coding[j][k]) {
                t = 0;
                break;
            }
        }
        if (t == 1) {
            append(out,c[j]);
            i = i + k - 1;
            break;
        }
    }
}
printf("%s\n",out);

//Huffman.c
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

typedef struct
{
    int data;
    struct BTreeNode* left;
    struct BTreeNode* right;
}BTreeNode;

#define M 32
char coding[M][M];

BTreeNode* CreateHuffman(int a[], int n)
{
    int i, j;
    BTreeNode **b, *q;
    b = malloc(n*sizeof(BTreeNode));
    for (i = 0; i < n; i++)     {
        b[i] = malloc(sizeof(BTreeNode));
        b[i]->data  = a[i];
        b[i]->left  = NULL;
        b[i]->right = NULL;
    }
    for (i = 1; i < n; i++)//进行 n-1 次循环建立哈夫曼树
    {
        int k1 = -1, k2;
        for (j = 0; j < n; j++)        {
            if (b[j] != NULL && k1 == -1)
            {
                k1 = j;
                continue;
            }
            if (b[j] != NULL)
            {
                k2 = j;
                break;
            }
        }
        for (j = k2; j < n; j++)//从当前森林中求出最小权值树和次最小
        {
            if (b[j] != NULL)
            {
                if (b[j]->data < b[k1]->data)
                {
                    k2 = k1;
                    k1 = j;
                }
                else if (b[j]->data < b[k2]->data)
                    k2 = j;
            }
        }
        q = malloc(sizeof(BTreeNode));
        q->data = b[k1]->data + b[k2]->data;
        q->left = b[k1];
        q->right = b[k2];

        b[k1] = q;
        b[k2] = NULL;
    }
    free(b);
    return q;
}

double WeightPathLength(BTreeNode* FBT, int len)//len初始为0
{
    if (FBT == NULL) {
        return 0;
    }
    else {
        if (FBT->left == NULL && FBT->right == NULL) {
            return FBT->data * len;
        }
        else {
            return WeightPathLength(FBT->left,len+1)+WeightPathLength(FBT->right,len+1);
        }
    }
}

static int index = 0;
char *c;
void HuffManCoding(FILE *fp, BTreeNode* FBT, int len)//len初始值为0
{
    static int a[10];
    if (FBT != NULL)    {
            if (FBT->left == NULL && FBT->right == NULL) {
                int i;
                fprintf(fp,"%c %d:",c[index++],FBT->data);
                for (i = 0; i < len; i++)
                    fprintf(fp,"%d", a[i]);
               fprintf(fp,"\n");
            }
            else {
                a[len] = 0;
                HuffManCoding(fp, FBT->left, len + 1);
                a[len] = 1;
                HuffManCoding(fp, FBT->right, len + 1);
            }
        }
}

void append(char *str, char ch) {
    int i;
    for (i = 0; str[i];i++);
    str[i] = ch;
    str[i+1] = ‘\0‘;
}

int main()
{
    int i, j, k, n, t;
    int* arr;
    char ch, in[M] = {‘\0‘}, code[M*M] = {‘\0‘}, out[M] = {‘\0‘};
    BTreeNode* fbt;
    FILE *fp;

    //Input
    freopen("test.in","r",stdin);
    scanf("%d", &n);
    arr = (int *)malloc(n * sizeof(int));
    c   = (char *)malloc(n * sizeof(char));
    arr[0] = 186;
    c[0] = ‘ ‘;
    //原谅楼主这里偷懒,空格字符的输入有点麻烦所以直接写入了
    for (i = 1; i < n; i++) {
        getchar();
        scanf("%c %d",&c[i],&arr[i]);
    }

    //huffman coding
    fbt = CreateHuffman(arr, n);
    fp = fopen("code.txt","w");
    HuffManCoding(fp, fbt, 0);
    fflush(fp);

    //Encoding
    fp = fopen("code.txt","r");
    for (i = 0; i < n; i++) {
       fgetc(fp);
       fscanf(fp,"%c %d:%s", &t, &ch, &coding[i]);
    }
    fp = fopen("src.in","r");
    fscanf(fp, "%s", in);
    for (i = 0; in[i]; i++) {
        for (j = 0; j < n; j++) {
            if (c[j] == in[i]) {
                strcat(code,coding[j]);
            }
        }
    }
    printf("%s\n",code);

    //Decoding
    for(i = 0; code[i]; i++) {
        for (j = 0; j < n; j++) {
            t = 1;
            for (k = 0; coding[j][k]; k++) {
                if (code[i + k] != coding[j][k]) {
                    t = 0;
                    break;
                }
            }
            if (t == 1) {
                append(out,c[j]);
                i = i + k - 1;
                break;
            }
        }
    }
    printf("%s\n",out);
    return 0;
}

测试数据:

test.in:

27
a 4
b 13
c 22
d 32
e 103
f 21
g 15
h 47
i 57
j 1
k 5
l 32
m 20
n 57
o 63
p 15
q 1
r 48
s 51
t 80
u 23
v 8
w 18
x 1
y 16
z 1
时间: 2024-10-09 17:13:54

Huffman树与编码的相关文章

Huffman树的编码译码

上个学期做的课程设计,关于Huffman树的编码译码. 要求: 输入Huffman树各个叶结点的字符和权值,建立Huffman树并执行编码操作 输入一行仅由01组成的电文字符串,根据建立的Huffman树进行译码操作,程序最后输出译码后的结果 Huffman.h定义了树的结点信息,各种操作.GCC编译通过. 1 #ifndef HUFFMAN_H_INCLUDED 2 #define HUFFMAN_H_INCLUDED 3 #include <iostream> 4 #include <

数据结构复习之利用Huffman树进行编码和译码

//编码#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> #include<queue> #include<fstream> #include<map> using namespace std; typedef struct HuffmanNode{ int w;//节点的权值

Huffman树及其编码(STL array实现)

这篇随笔主要是Huffman编码,构建哈夫曼树有各种各样的实现方法,如优先队列,数组构成的树等,但本质都是堆. 这里我用数组来存储数据,以堆的思想来构建一个哈弗曼树,并存入vector中,进而实现哈夫曼编码 步骤: 1生成哈夫曼树  (取最小权值树和次小权值树生成新树,排列后重新取树,不断重复)    2编码   (遵循左零右一的原则)           3解码(是编码的逆向,本文还未实现,日后有机会补充) data.txt  测试数据: 51 2 3 4 5abcde 结果: 下面贴代码:

Huffman树费用

问题描述 Huffman树在编码中有着广泛的应用.在这里,我们只关心Huffman树的构造过程. 给出一列数{pi}={p0, p1, -, pn-1},用这列数构造Huffman树的过程如下: 1. 找到{pi}中最小的两个数,设为pa和pb,将pa和pb从{pi}中删除掉,然后将它们的和加入到{pi}中.这个过程的费用记为pa + pb. 2. 重复步骤1,直到{pi}中只剩下一个数. 在上面的操作过程中,把所有的费用相加,就得到了构造Huffman树的总费用. 本题任务:对于给定的一个数列

Huffman树及其编解码

Huffman树--编解码 介绍: ??Huffman树可以根据输入的字符串中某个字符出现的次数来给某个字符设定一个权值,然后可以根据权值的大小给一个给定的字符串编码,或者对一串编码进行解码,可以用于数据压缩或者解压缩,和对字符的编解码. ??可是Huffman树的优点在哪? ??1.就在于它对出现次数大的字符(即权值大的字符)的编码比出现少的字符编码短,也就是说出现次数越多,编码越短,保证了对数据的压缩. ??2.保证编的码不会出现互相涵括,也就是不会出现二义性,比如a的编码是00100,b的

哈夫曼(Huffman)树与哈夫曼编码

声明:原创作品,转载时请注明文章来自SAP师太技术博客:www.cnblogs.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将追究法律责任!原文链接:http://www.cnblogs.com/jiangzhengjun/p/4289610.html 哈夫曼树又称最优二叉树,是一种带权路径长最短的树.树的路径长度是从树根到每一个叶子之间的路径长度之和.节点的带树路径长度为从该节点到树根之间的路径长度与该节点权(比如字符在某串中的使用频率)的乘积. 比如有一串字符串如

哈夫曼(Huffman)树和哈夫曼编码

一.哈夫曼(Huffman)树和哈夫曼编码 1.哈夫曼树(Huffman)又称最优二叉树,是一类带权路径长度最短的树, 常用于信息检测. 定义: 结点间的路径长度:树中一个结点到另一个结点之间分支数目称为这对结点之间的路径长度. 树的路径长度:树的根结点到树中每一结点的路径长度之和. 带权路径长度:从根结点到某结点的路径长度与该结点上权的乘积. 树的带权路径长度:树中所有叶子结点的带权路径长度之和记为WPL. 例如: 对图(a): WPL =9×2+5×2+2×2+3×2=38 对图(b): W

数据结构之huffman树

#include <stdio.h> #include<stdlib.h> #define MAXBIT      99 #define MAXVALUE  9999 #define MAXLEAF     30 #define MAXNODE    MAXLEAF*2 -1 typedef struct  {     int bit[MAXBIT];     int start; } HCodeType;        /* 编码结构体 */ typedef struct {  

数据结构之Huffman树与最优二叉树

最近在翻炒一些关于树的知识,发现一个比较有意思的二叉树,huffman树,对应到离散数学中的一种名为最优二叉树的路径结构,而Huffman的主要作用,最终可以归结到一种名为huffman编码的编码方式,使用huffman编码方式,我们可以以平均长度最短的码字来记录一串信息,且每个信息分子的编码唯一,独立.从而最终合成编码所对应的信息唯一,无歧义. huffman树的创建时基于每个信息分子都拥有其权重,权重越大,越靠近树根,即路径越短, 下面我们我们来以一个huffman树的例子为例:简单引入一下