zoukankan      html  css  js  c++  java
  • C++哈弗曼编码

      1 // haffman.cpp : 定义控制台应用程序的入口点。
      2 //
      3 
      4 #include "stdafx.h"
      5 #include<iostream>
      6 using namespace std;
      7 
      8 typedef struct hfnode
      9 {
     10     char ch;      //存储字符
     11     int weight;   //存储权值
     12     int parent;   //双亲结点位置
     13     int lchild;   //左右孩子结点位置
     14     int rchild;
     15 } hfnode,*hfmtree;
     16 
     17 typedef struct node
     18 {
     19     char code[10];
     20 } node,*hfcode;
     21 
     22 #define Nsymbols 10
     23 
     24 hfnode *Inithfm(hfnode *tree,int n)  //千万不能用关键字命名。把这些节点作为带权值的二叉树的根节点,左右子树为空
     25 {
     26     //tree = new hfnode[n]; //多余了,已经在main函数中声明了。
     27     for(int i = 0;i < n; i++)
     28     {
     29         tree[i].ch = '0';
     30         tree[i].weight = 0;
     31         tree[i].lchild = 0;
     32         tree[i].rchild = 0;
     33         tree[i].parent = 0;
     34     }
     35     cout << "请输入想要编码的字符序列,按照先字符后次数的顺序输入" << endl;
     36     for(int i = 0;i < 4; i++)
     37     {
     38         cin >> tree[i].ch >>tree[i].weight;
     39     }
     40      cout <<endl; 
     41 
     42     for(int i = 0;i < 4;i++)
     43         cout << tree[i].ch << " "<<tree[i].weight<<" ";
     44     cout << endl;
     45     return tree;
     46 }
     47 
     48 void select(hfnode *tree,int n,int *p1,int *p2) //选择两棵根结点权值最小的树作为左右子树构造一棵新的二叉树,且至新的二叉树的根结点的权值为其左右子树上根结点的权值之和。
     49 {
     50     //找一个作为参考,返回的是结点
     51     int x,y;
     52     for(int i = 0;i <= n;i++)
     53     {
     54         if(tree[i].parent==0)
     55         {
     56             x = i;
     57             break;//找到一个参考点即可
     58         }
     59     }
     60 
     61 
     62     for(int i = 0;i <= n;i++)  //找出最小的结点
     63     {
     64         if((tree[i].parent==0)&&(tree[i].weight!=0))
     65         {
     66             if(tree[i].weight < tree[x].weight)
     67                 x = i;
     68         }
     69     }
     70 
     71    for(int j = 0;j <= n;j++)
     72     {
     73         if((tree[j].parent==0)&&(j!=x))
     74         {
     75             y = j;
     76             break;//找到一个参考点即可
     77         }
     78     }
     79    for(int j = 0;j <= n;j++)   //找出次小的结点
     80     {
     81         if((tree[j].parent==0)&&(tree[j].weight!=0)&&j!=x)
     82         {
     83             if(tree[j].weight < tree[y].weight)
     84                 y = j;
     85         }
     86     }
     87        *p1 = x;
     88        *p2 = y;
     89 }
     90 //对哈弗曼树进行编码。
     91 
     92 void HfCode(hfnode *tree,int n,node *hfmcode,int m)
     93 {
     94      int c,f;
     95      int start = 0;
     96 
     97      for(int i=0;i<10;i++)
     98      {
     99          for(c=i,f=tree[c].parent;f!=0;c=f,f=tree[c].parent) //追溯法,从叶子节点出发,一路往上追溯。
    100          {
    101              if(tree[f].lchild==c)
    102                  hfmcode[i].code[start++] = '0';
    103              else 
    104                  hfmcode[i].code[start++] = '1';
    105          }
    106          start = 0;
    107      }   
    108 }
    109 
    110 void Print(node *hfmcode,int m)
    111 {
    112     for(int i = 0;i < m;i++)
    113     {
    114         for(int j = 0;j < m;j++)
    115           cout <<hfmcode[i].code[j];
    116         cout << endl;
    117     }
    118     cout << endl;
    119 }
    120 
    121 
    122 
    123 int main()
    124 {
    125     int p1=0,p2=0; //为了接收最小和次小的两个节点
    126     hfnode tree[Nsymbols]; //当我们不是用指针,这种情况下,已经全部赋值了。不用再一次赋值了。
    127     Inithfm(tree,Nsymbols); //初始化结构体数组。
    128   
    129     //建立哈弗曼树
    130 
    131     for(int m = 4;m < 2*4-1;m++) //二叉树性质
    132     {
    133         select(tree,m-1,&p1,&p2);
    134         tree[p1].parent = m;
    135         tree[p2].parent = m;
    136         tree[m].lchild = p1;
    137         tree[m].rchild = p2;
    138         tree[m].weight = tree[p1].weight + tree[p2].weight;
    139         tree[m].parent =0; //其实这句也可以不用,因为初始化时已经初始化为0了。
    140     }
    141 
    142    node hfmcode[4];
    143 // 初始化,不然打印的时候会出现未知错。
    144    for(int i = 0;i < 4;i++)
    145     {
    146         for(int j = 0;j < 4;j++)
    147          hfmcode[i].code[j]='';
    148     }
    149 
    150    HfCode(tree,Nsymbols,hfmcode,4); //编码函数
    151    Print(hfmcode,4);  //打印函数
    152     return 0;
    153 }

    实验名称:哈弗曼编码

    实验目的:了解前缀编码的概念,理解数据压缩的基本方法;

          掌握Huffman编码的设计思想并能熟练应用。

    实验要求:对有字符集{A,B,C,D},各字符在电文中出现的次数集为{1,3,4,7};

    要求编程实现Huffman树的构造,并在此基础上编程实现Huffman编码。

    实验步骤及内容

    1、首先建立一个定义哈弗曼树的结构体Node,及结构体指针LinkList,该结构体包含一个存储字符的ch,存储权值的weight,存储双亲结点的parent,存储左右孩子的lchild与rchild。代码如下:

       typedef struct hfnode

    {

        char ch;      //存储字符

        int weight;   //存储权值

        int parent;   //双亲结点位置

        int lchild;   //左右孩子结点位置

        int rchild;

    } hfnode,*hfmtree;

    2、定义一个存储编码的结构体,主要是为了方便打印,代码如下:

     typedef struct node

    {

        char code[10];

    } node,*hfcode;

     

    3、初始化函数,因为默认值是一个不确定的值,必须进行初始化才行。

    hfnode *Inithfm(hfnode *tree,int n)  //千万不能用关键字命名。把这些节点作为带权值的二叉树的根节点,左右子树为空

    {

        //tree = new hfnode[n]; //多余了,已经在main函数中声明了。

        for(int i = 0;i < n; i++)

        {

            tree[i].ch = '0';

            tree[i].weight = 0;

            tree[i].lchild = 0;

            tree[i].rchild = 0;

            tree[i].parent = 0;

        }

        cout << "请输入想要编码的字符序列,按照先字符后次数的顺序输入" << endl;

        for(int i = 0;i < 4; i++)

        {

            cin >> tree[i].ch >>tree[i].weight;

        }

         cout <<endl;

     

        for(int i = 0;i < 4;i++)

            cout << tree[i].ch << " "<<tree[i].weight<<" ";

        cout << endl;

        return tree;

    }

    4、选择两棵根结点权值最小的树作为左右子树。

    void select(hfnode *tree,int n,int *p1,int *p2) //选择两棵根结点权值最小的树作为左右子树构造一棵新的二叉树,且至新的二叉树的根结点的权值为其左右子树上根结点的权值之和。

    {

        //找一个作为参考,返回的是结点

        int x,y;

        for(int i = 0;i <= n;i++)

        {

            if(tree[i].parent==0)

            {

                x = i;

                break;//找到一个参考点即可

            }

        }

     

     

        for(int i = 0;i <= n;i++)  //找出最小的结点

        {

            if((tree[i].parent==0)&&(tree[i].weight!=0))

            {

                if(tree[i].weight < tree[x].weight)

                    x = i;

            }

        }

     

       for(int j = 0;j <= n;j++)

        {

            if((tree[j].parent==0)&&(j!=x))

            {

                y = j;

                break;//找到一个参考点即可

            }

        }

       for(int j = 0;j <= n;j++)   //找出次小的结点

        {

            if((tree[j].parent==0)&&(tree[j].weight!=0)&&j!=x)

            {

                if(tree[j].weight < tree[y].weight)

                    y = j;

            }

        }

           *p1 = x;

           *p2 = y;

    }

    5、构造哈弗曼树

            //建立哈弗曼树

     

        for(int m = 4;m < 2*4-1;m++) //二叉树性质

        {

            select(tree,m-1,&p1,&p2);

            tree[p1].parent = m;

            tree[p2].parent = m;

            tree[m].lchild = p1;

            tree[m].rchild = p2;

            tree[m].weight = tree[p1].weight + tree[p2].weight;

            tree[m].parent =0; //其实这句也可以不用,因为初始化时已经初始化为了。

        }

    6、对哈弗曼树进行编码。编码函数如下:

        //对哈弗曼树进行编码。

     

    void HfCode(hfnode *tree,int n,node *hfmcode,int m)

    {

         int c,f;

         int start = 0;

     

         for(int i=0;i<10;i++)

         {

             for(c=i,f=tree[c].parent;f!=0;c=f,f=tree[c].parent) //追溯法,从叶子节点出发,一路往上追溯。

             {

                 if(tree[f].lchild==c)

                     hfmcode[i].code[start++] = '0';

                 else

                     hfmcode[i].code[start++] = '1';

             }

             start = 0;

         }  

    }

    7、打印编码

     

    void Print(node *hfmcode,int m)

    {

        for(int i = 0;i < m;i++)

        {

            for(int j = 0;j < m;j++)

              cout <<hfmcode[i].code[j];

            cout << endl;

        }

        cout << endl;

    }

    另配上一副自己用画图工具画的理论分析图:

    结果与C++代码结果一致。

    总结:

        此次实验,让我理解了结构体数组的使用以及初始化,内存管理等方面的熟悉,感觉收获挺大的。

  • 相关阅读:
    设计模式之工厂模式
    Java内存区域与内存溢出异常
    Spark环境搭建
    Android获取蓝牙地址
    Intent和BroadcastReceiver
    Fragment初探
    Acticity的生命周期和启动模式
    Maven依赖,去哪儿找
    Spring-BeanDefinition
    Spring-BeanFactory体系介绍
  • 原文地址:https://www.cnblogs.com/zhuxuekui/p/3474769.html
Copyright © 2011-2022 走看看