数据结构课设——哈弗曼编码/译码系统(树应用)

问题描述

利用哈夫曼编码进行通信,可以压缩通信的数据量,提高传输效率,缩短信息的传输时间,还有一定的保密性。现在要求编写一程序模拟传输过程,实现在发送前将要发送的字符信息进行编码,然后进行发送,接收后将传来的数据进行译码,即将信息还原成发送前的字符信息。

算法思想

建立两个结构体,HTNode储存哈夫曼树信息,frequence储存字符的相关信息。以#号键作为输入的结束条件,并用map记录各个字符的出现次数,保存下字符种类的总数,遍历map,给frequence类型的数组赋值,计算频数保存为哈夫曼树的权值。创建哈弗曼树时先对哈弗曼树的信息进行初始化,之后选择两个权值最小的结点,for循环,i从字符种类加一开始,将i赋给权值最小的两个结点的双亲,将权值最小的两个结点的序号分别赋给第i个结点的左孩子和右孩子,两个最小权值相加即为i的权值。编码是从叶子结点到根结点遍历,逐个求解n个字符的编码,循环n次。译码则是从根结点到叶子结点,逐个求解编码对应的n个字符,循环n次。

算法设计

1、菜单void menu()——输出功能选择。

2、记录输入的字符信息void record(HuffmanTree &HT,fre &fq)——以#号键作为输入的结束条件,并用map记录各个字符的出现次数,保存下字符种类的总数,遍历map,给frequence类型的数组赋值,计算频数保存为哈夫曼树的权值。

3、选择结点void Select(HuffmanTree &HT,int n,int &s1,int &s2)——选择两个权值最小的结点。

4、创建哈弗曼树void CreateHuffmanTree(HuffmanTree &HT)——创建哈弗曼树时先对哈弗曼树的信息进行初始化,之后选择两个权值最小的结点,for循环,i从字符种类加一开始,将i赋给权值最小的两个结点的双亲,将权值最小的两个结点的序号分别赋给第i个结点的左孩子和右孩子,两个最小权值相加即为i的权值。

5、编码void CreateHuffmanCode(HuffmanTree HT,HuffmanCode &HC,fre &fq)——从叶子结点到根结点遍历,逐个求解n个字符的编码,循环n次

6、译码void DeHuffmanCode(HuffmanTree HT,HuffmanCode &HC,fre &fq)——从根结点到叶子结点,逐个求解原码对应的字符,直到访问到’\0’。

7、主函数int main()——使用while循环和switch对功能进行调用,使用清屏函数,使界面更理想。

代码实现

#include<stdio.h>//实验三
#include<iostream>
#include<algorithm>
#include<string.h>
#include<map>
using namespace std;
const int INT=100000000;
typedef struct{//定义哈弗曼树
    int weight;//权值
    int parent,lchild,rchild;
}HTNode,*HuffmanTree;
typedef struct{//统计频率
    char c;//字符
    int times;//字符出现次数
}frequence,*fre;
typedef char **HuffmanCode;
char s[51];
int sum=0;int length;
void menu(){
    printf("***********哈弗曼编译码系统***********\n");
    printf("           1、编码系统\n");
    printf("           2、译码系统\n");
    printf("           3、退出\n");
    printf("**************************************\n");
    printf("请选择...\n");
}
void record(HuffmanTree &HT,fre &fq){//记录输入的字符信息
    map<char,int>m;//用map获得字符出现次数
    printf("请输入字符信息(以#号键结束输入):");
    int i;
    for(i=0;;++i)
    {
        scanf("%c",&s[i]);
        if(s[i]=='#'){//输入结束的控制条件
            s[i]='\0';
            break;
        }
        else{
            m[s[i]]++;
        }
    }
    length=i;//记录字符总数
    sum=m.size();//记录字符种类总数
    int j=1;
    fq=new frequence[sum+1];
    HT=new HTNode[2*sum+1];
    map<char,int>::iterator iter;
    cout<<"字符种类数为:"<<m.size()<<endl;
    for(iter=m.begin();iter!=m.end();iter++){
        cout<<"字符"<<iter->first<<": ";
        cout<<iter->second<<"次"<<endl;
        fq[j].c=iter->first;
        fq[j].times=iter->second;
        HT[j].weight=100*fq[j].times/length;//储存字符出现的频数
        j++;
    }
}
void Select(HuffmanTree &HT,int n,int &s1,int &s2){//选择两个权值最小的结点
    int s11=INT+1;
    int s22=INT+1;
    for(int j=1;j<=n;j++){
        if(HT[j].parent==0&&s11>HT[j].weight)
        {
            s11=HT[j].weight;
            s1=j;
        }
    }
    for(int j=1;j<=n;j++){
        if(j==s1)
            continue;
        if(HT[j].parent==0&&s22>HT[j].weight&&s11<=HT[j].weight)
        {
            s22=HT[j].weight;
            s2=j;
        }
    }
}
void CreateHuffmanTree(HuffmanTree &HT){//创建哈弗曼树
    int i;
    int s1=1,s2=1,w=sum;
    for(i=1;i<=(2*w-1);i++){
        HT[i].parent=0;//初始化
        HT[i].lchild=0;
        HT[i].rchild=0;
        if(i>w)
            HT[i].weight=INT+1;
    }
    cout<<endl<<"哈弗曼树编码次序为:"<<endl;
    for(i=w+1;i<=(2*w-1);++i){
        Select(HT,i-1,s1,s2);
        HT[s1].parent=i;//将i赋给权值最小的两个结点的双亲
        HT[s2].parent=i;
        HT[i].lchild=s1;//将权值最小的两个结点的序号分别赋给第i个结点的左孩子和右孩子
        HT[i].rchild=s2;
        HT[i].weight=HT[s1].weight+HT[s2].weight;//两个最小权值相加即为i的权值
        cout << "HT[" << s1 << "] and HT[" << s2 << "] create";
        cout << " HT[" << i << "], weight=" << HT[i].weight << endl;
    }
    cout<<endl;
}
void CreateHuffmanCode(HuffmanTree HT,HuffmanCode &HC,fre &fq){//编码
    int n=sum;
    HC=new char*[n+1];
    char *cd;
    cd=new char[n];
    cd[n-1]='\0';
    for(int i=1;i<=n;++i){
        int start=n-1;
        int c,f;
        c=i;
        f=HT[i].parent;
        while(f!=0)
        {
            --start;
            if(HT[f].lchild==c)
                cd[start]='0';
            else
                cd[start]='1';
            c=f;
            f=HT[f].parent;
        }
        HC[i]=new char[n-start];
        strcpy(HC[i],&cd[start]);
        cout<<"字符"<<fq[i].c<<"的编码:"<<HC[i]<<endl;
    }
    cout<<endl;
    cout<<"编码信息:";
    for(int i=0;i<length;++i){
        for(int j=1;j<=sum;j++)
            if(s[i]==fq[j].c)
                cout<<HC[j];
    }
    cout<<endl<<endl;
    delete cd;
}
void DeHuffmanCode(HuffmanTree HT,HuffmanCode &HC,fre &fq)//译码
{
    //cout<<"字符信息为:";
    //printf("%s\n",s);
    cout<<"请输入需要破译的原码:";
    char late[51];
    int p=2*sum-1,i=0;
    scanf("%s",late);
    cout<<"译码为:";
    while (late[i] != '\0' && p != 0)
    {
        if (late[i] == '0')
            p = HT[p].lchild;   //走向左孩子
        else
            p = HT[p].rchild;   //走向右孩子
        if (!HT[p].lchild && !HT[p].rchild)     //tree[i]是叶结点
        {
            cout << fq[p].c;
            p = 2 * sum - 1;      //回到根结点
        }
        i++;

    }
    cout << endl;
    if (HT[i].lchild&&late[i]!= '\0')
        cout << "\n输入原码错误\n";
}
int main(){
    HuffmanTree HT;
    HuffmanCode HC;
    fre fq;
    int m;
    while(m!=3){
        menu();
        cin>>m;
        getchar();
        switch(m){
        case 1:
            record(HT,fq);
            CreateHuffmanTree(HT);
            CreateHuffmanCode(HT,HC,fq);
            break;
        case 2:
             DeHuffmanCode(HT,HC,fq);
             break;
        case 3:
            cout<<"感谢您的使用!"<<endl;
            return 0;
        default:
            cout<<"没有该选项!"<<endl;
        }
        system("pause");
        system("cls");
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/DEAR_CXN/article/details/86555910