Huffman 编码的编程与实现 C语言

一 、目的:
1.    掌握Huffman树的概念、存储结构;
2.    掌握建立Huffman树和Huffman编码的方法;
二 、环境:
operating system version:Win11
CPU instruction set:  x64
Integrated Development Environment:Viusal Studio 2022
三 、内容:
利用动态分配数组存储赫夫曼树,设计一组输入数据(假定为一组整数),能够对其进行如下操作:
1)创建一个新的顺序表,实现动态空间分配的初始化
2)对输入的数据构造成一棵 Huffman 树;
3)根据生成的 Huffman 树进行 Huffman 编码
4)实现对输入数据的 Huffman 编码输出 。
四 、要求:
1) 实现 Huffman 树的生成
2) 完成 Huffman 编码的输出 。
五 、步骤:
1. 程序:
 

#include<iostream>  
#include<stdio.h>  
#include<stdlib.h>  
#include<string.h>  
  
using namespace std;  
typedef char ElemType;  
typedef struct {  
    ElemType elem;  
    unsigned int weight;  
    unsigned int parent, lchild, rchild, tag;  
}HTNode, * HuffmanTree;  
typedef char** HuffmanCode;  
  
typedef int Status;  
typedef struct weight  
{  
    char elem;  
    unsigned int m_weight;  
}Weight;   
  
void Select(HuffmanTree HT, int n, int& s1, int& s2)  
{  
    int i;  
    s1 = s2 = 0;  
    for (i = 1; i <= n; i++) {  
        if (HT[i].weight < HT[s2].weight && HT[i].parent == 0 && s2 != 0) {  
            if (HT[i].weight < HT[s1].weight) {  
                s2 = s1;    s1 = i;  
            }  
            else s2 = i;  
        }  
        if ((s1 == 0 || s2 == 0) && HT[i].parent == 0) {  
            if (s1 == 0) s1 = i;  
            else if (s2 == 0) {  
                if (HT[i].weight < HT[s1].weight) {  
                    s2 = s1;  s1 = i;  
                }  
                else s2 = i;  
            }   
        }    
    }   
    if (s1 > s2) {  
        i = s1; s1 = s2; s2 = i;  
    }  
}  
  
void HuffmanCoding(HuffmanTree& HT, HuffmanCode& HC, Weight*& w, int n) {  
  
   // w存放n个字符的权值(均>0),构造哈夫曼树HT,  
   // 并求出n个字符的哈夫曼编码HC  
  //本函数实现选择方式:从左往右选择两个小权值结点,结点信息在前面的为左子树,其后为右子树  
  
    int i, j, m, s1, s2;  
    char* cd;  
    int p;  
    int cdlen;  
  
    if (n <= 1) return;  
    m = 2 * n - 1;  
    HT = (HuffmanTree)malloc((m + 1) * sizeof(HTNode));   // 0号单元未用  
    for (i = 1; i <= n; i++) { //初始化  
        HT[i].elem = w[i - 1].elem;  
        HT[i].weight = w[i - 1].m_weight;  
        HT[i].parent = 0;  
        HT[i].lchild = 0;  
        HT[i].rchild = 0;  
    }  
    for (i = n + 1; i <= m; i++) { //初始化  
        HT[i].weight = 0;  
        HT[i].parent = 0;  
        HT[i].lchild = 0;  
        HT[i].rchild = 0;  
    }  
    printf("\n哈夫曼树的构造过程如下所示:\n");  
    printf("HT初态:\n  结点  weight  parent  lchild  rchild");  
    for (i=1; i<=m; i++)  
       printf("\n%4d%8d%8d%8d%8d",i,HT[i].weight,  
              HT[i].parent,HT[i].lchild, HT[i].rchild);  
    getchar();  
      
    for (i = n + 1; i <= m; i++) {     // 建哈夫曼树  
      // 在HT[1..i-1]中选择parent为0且weight最小的两个结点,  
      // 其序号分别为s1和s2。  
        Select(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;  
  
        printf("\nselect: s1=%d   s2=%d\n", s1, s2);  
        printf("  结点  weight  parent  lchild  rchild");  
        for (j=1; j<=i; j++)  
          printf("\n%4d%8d%8d%8d%8d",j,HT[j].weight,  
                 HT[j].parent,HT[j].lchild, HT[j].rchild);  
        getchar();  
  
    }  
  
   //------无栈非递归遍历哈夫曼树,求哈夫曼编码  
    HC = (HuffmanCode)malloc((n + 1) * sizeof(char*));  
    cd = (char*)malloc((n + 1) * sizeof(char)); // 分配求编码的工作空间  
    p = m;  cdlen = 0;  
    for (i = 1; i <= m; ++i)  // 遍历哈夫曼树时用作结点状态标志  
        HT[i].tag = 0;  
    while (p) {  
        if (HT[p].tag == 0) { // 向左  
            HT[p].tag = 1;  
            if (HT[p].lchild != 0) { p = HT[p].lchild;  cd[cdlen++] = '0'; }  
            else if (HT[p].rchild == 0) {  
                HC[p] = (char*)malloc((cdlen + 1) * sizeof(char));  
                cd[cdlen] = '\0';  strcpy(HC[p], cd);  
            }  
        }  
        else if (HT[p].tag == 1) {        // 向右  
            HT[p].tag = 2;  
            if (HT[p].rchild != 0) { p = HT[p].rchild;  cd[cdlen++] = '1'; }  
        }  
        else {   // HT[p].weight==2,退回退到父结点,编码长度减1  
            HT[p].tag = 0; p = HT[p].parent; --cdlen;  
        }  
    }  
}   
  
  
  
void OutputHuffmanCode(HuffmanTree HT, HuffmanCode HC, int n)  
{  
    int i;  
    printf("\nnumber---element---weight---huffman code\n");  
    for (i = 1; i <= n; i++)  
        printf("  %d        %c         %d        %s\n", i, HT[i].elem, HT[i].weight, HC[i]);  
}  
  
int  main()  
{  
    HuffmanTree HT;  
    HuffmanCode HC;  
    Weight* w;  
    char c;     // the symbolizes;  
    int i, n;      // the number of elements;   
    int wei;    // the weight of a element;   
  
    printf("请输入准备构建哈夫曼树的结点数:");  
    cin >> n; //cout<<endl;  
    w = (Weight*)malloc(n * sizeof(Weight));  
    for (i = 0; i < n; i++)  
    {  
        printf("请输入结点编号与对应的权值(node weight):");  
        scanf("%1s%d", &c, &wei);  
        w[i].elem = c;  
        w[i].m_weight = wei;  
    }  
  
    HuffmanCoding(HT, HC, w, n);  
    OutputHuffmanCode(HT, HC, n);  
  
}  

2.程序结果:

1)程序运行,我使用的测试数据如下所示:

2)首先按照要求完成了哈夫曼树的构建:

3)最终完成哈夫曼编码,结果与预期一致。

六 、小结:

       此次是关于哈夫曼树的编程与实现,通常给定N个权值作为N个叶子结点,构造一棵二叉树,若该树的带权路径长度达到最小,称这样的二叉树为最优二叉树,也称为哈夫曼树(Huffman Tree)。哈夫曼树是带权路径长度最短的树,权值较大的结点离根较近。

        我先输入了权值的 n 个结点,并且在构建哈夫曼树时我采用了如下办法:首先在 n 个权值中选出两个最小的权值,对应的两个结点组成一个新的二叉树,且新二叉树的根结点的权值为左右孩子权值的和;在原有的 n 个权值中删除那两个最小的权值,同时将新的权值加入到 n–2 个权值的行列中,以此类推;重复上述过程,直到所以的结点构建成了一棵二叉树为止,就得到了哈夫曼树。通常查找权重值最小的两个结点的思想是:从树组起始位置开始,首先找到两个无父结点的结点(说明还未使用其构建成树),然后和后续无父结点的结点依次做比较,有两种情况需要考虑:如果比两个结点中较小的那个还小,就保留这个结点,删除原来较大的结点;如果介于两个结点权重值之间,替换原来较大的结点。最终实现了描述的功能。

猜你喜欢

转载自blog.csdn.net/m0_52316372/article/details/125707349