中南大学数据结构实验四

前言:必须先理解Huffuman树的构建过程,可以参考其他人写的博客:
哈夫曼树以及哈夫曼编码的构造步骤
实验四 Huffman编码
一、实验目的
1、理解哈夫曼树及其应用。
2、掌握生成哈夫曼树的算法。
二、实验内容
设字符集为26个英文字母,其出现频度如下表所示。以二叉链表作存储结构,编写程序,实现如下的功能:
1、根据所提供的字母数据建立一个Huffman树;
2、根据生成的Huffman树的结构,显示输出所有字母的Huffman编码。
3、(选作内容)根据产生的Huffman编码,实现Huffman编/译码器。
三、 实验要求
1.认真阅读和掌握本实验的算法。
2.上机将本算法实现。
3.在程序的编写中尽量与专业的编程规范靠拢,系统代码采用结构化的编程方式,力求设计代码以及注释等规范,
4.保存和打印出程序的运行结果,并结合程序进行分析。
四、实验代码
列出了三种写法

//       huffuman树是最小路径之和,所以huffuman树是不唯一的
//       每合并两个节点就得对权值重新进行排序,所以这里可以利用优先队列(最小堆)每次队头(堆顶)元素就是就是最小值
//       也可以用数组
// 输入格式
// 第一行包含一个整数n,表示输入的字符数量
// 第二行包含2n个字符,分别表示输入的字母以及出现次数,输入格式如下
// 26
// a 168 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
/*
//写法1:利用优先队列实现
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;
string strs[26];//存放最终每个字母生成的huffuman编码
struct Node{
    //定义结构体用来生成节点
    int val;
    char word;
    string c;//存储生成的Huffman编码
    Node* left;
    Node* right;
};
struct cmp {
    //重载 () 运算符
    bool operator ()(const Node* a, const Node* b) {
        return  a->val>b->val;
    }
};
//根据生产的huffuman树生产每个字母的huffuman编码
void PreOrder(Node* root,string s)//这里是前序遍历
{
    if(root==NULL)
        return ;
    root->c=s;
    if(root->word!=' ')//只添加叶子节点也就是字母所在的节点
        strs[root->word-'a']=s;
    PreOrder(root->left,s+"0");//往左走则添加0
    PreOrder(root->right,s+"1");//往右走则添加1
}
string stringToHufuman(string words)//huffuman编码器
{
    string ans="";
    for(int i=0;i<words.length();i++)
        ans+=strs[words[i]-'a'];
    return ans;
}
string huffumanToString(string password,Node* root)//huffuman解码器,解码Huffuman编码
{
    string ans="";
    Node* head=root;
    for(int i=0;i<password.length();i++)
    {
        if(head->left==NULL&&head->right==NULL)//找到一个字母的Huffuman编码
        {
            ans+=head->word;
            head=root;//将head重置为root,开启下一轮查找
        }
        if(password[i]=='0')
            head=head->left;
        else
            head=head->right;
    }
    if(head->left==NULL&&head->right==NULL)//还需判断一下不然会漏掉最后一个字母
        ans+=head->word;
    return ans;
}
int main()
{
    int n;
    cin>>n;
    //定义一个优先队列(最小堆)
    priority_queue<Node*,vector<Node*>,cmp> q;
    for(int i=0;i<n;i++)
    {
        char word;
        int cnt;
        cin>>word>>cnt;
        Node* node=new Node{cnt,word,"",NULL,NULL};
        q.push(node);
    }
    while(q.size()>1)//如果只剩下一个节点那么就是最终的根节点
    {
        Node* parent=new Node{};
        Node* node1=q.top();
        q.pop();
        Node* node2=q.top();
        q.pop();
        parent->word=' ';//将合成得出来的节点字母设置为空格
        parent->right=node1;//出现次数最少的字母放在右边(也可以放在左边,放在左边和放在右边产生的编码正好相反)
        parent->left=node2;
        parent->val=node1->val+node2->val;
        q.push(parent);
    }
    for(int i=0;i<26;i++)
        strs[i]="";
    PreOrder(q.top(),"");
    for(int i=0;i<26;i++)
    {
        if(strs[i]!="")
            cout<<char (i+'a')<<"的huffuman编码为:"<<strs[i]<<endl;
    }
    cout<<stringToHufuman("zhongnandaxue")<<endl;
    cout<<huffumanToString("00101101011100010001110101100111000011100110000001011011011010100",q.top())<<endl;
}
*/
/*
//写法2:利用结构体数组实现+自带排序函数sort实现
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;
string strs[26];//存放最终每个字母生成的huffuman编码,这里26只考虑了小写字母
struct Node{
    //定义结构体用来生成节点
    int val;//出现频率
    char word;//存储字母
    string c;//存储生成的Huffman编码
    Node* left;
    Node* right;
    //重载操作<,这样利用c++自带的sort函数可以实现依据val的值从小到大排序。也可以根据val的大小,手写一个结构体排序
    bool operator <(const Node &a)const {
        return val<a.val;
    }
};
Node nodes[100];//定义结构体数组存储产生的节点
//根据生产的huffuman树生产每个字母的huffuman编码
void PreOrder(Node* root,string s)//这里是前序遍历
{
    if(root==NULL)
        return ;
    root->c=s;
    if(root->word!=' ')//只添加叶子节点也就是字母所在的节点
        strs[root->word-'a']=s;
    PreOrder(root->left,s+"0");//往左走则添加0
    PreOrder(root->right,s+"1");//往右走则添加1
}
string stringToHufuman(string words)//huffuman编码器
{
    string ans="";
    for(int i=0;i<words.length();i++)
        ans+=strs[words[i]-'a'];
    return ans;
}
string huffumanToString(string password,Node* root)//huffuman解码器,解码Huffuman编码
{
    string ans="";
    Node* head=root;
    for(int i=0;i<password.length();i++)
    {
        if(head->left==NULL&&head->right==NULL)//找到一个字母的Huffuman编码
        {
            ans+=head->word;
            head=root;//将head重置为root,开启下一轮查找
        }
        if(password[i]=='0')
            head=head->left;
        else
            head=head->right;
    }
    if(head->left==NULL&&head->right==NULL)//还需判断一下不然会漏掉最后一个字母
        ans+=head->word;
    return ans;
}
int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        char word;
        int cnt;
        cin>>word>>cnt;
        nodes[i]={cnt,word,"",NULL,NULL};
    }
    for(int i=n;i<100;i++)
        nodes[i]={1000000000,' ',"",NULL,NULL};//将多余的节点val设置为一个很大的数就不会影响排序结果
    sort(nodes,nodes+100);
    int idx=0;//设置排序的偏移量
    while(nodes[idx+1].val!=1e9)//至少还存在两个未合并的节点就一直循环
    {
        nodes[99].val=nodes[0+idx].val+nodes[1+idx].val;
        nodes[99].c=' ';
        nodes[99].left=&nodes[1+idx];//其次小的放在左子树
        nodes[99].right=&nodes[0+idx];//最小的放在右子树
        idx+=2;
        sort(nodes+idx,nodes+100);
    }
    for(int i=0;i<26;i++)
        strs[i]="";
    Node* root=&nodes[idx];
    PreOrder(root,"");
    for(int i=0;i<26;i++)
    {
        if(strs[i]!="")
            cout<<char (i+'a')<<"的huffuman编码为:"<<strs[i]<<endl;
    }
    cout<<stringToHufuman("zhongnandaxue")<<endl;
    cout<<huffumanToString("00101101111100010001100101010110000011000111000001011011011010100",root)<<endl;
}
*/

//写法三:利用结构体数组实现+自定义排序实现
#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;
string strs[26];//存放最终每个字母生成的huffuman编码,这里26只考虑了小写字母
struct Node{
    //定义结构体用来生成节点
    int val;//出现频率
    char word;//存储字母
    string c;//存储生成的Huffman编码
    Node* left;
    Node* right;
};
Node nodes[100];//定义结构体数组存储产生的节点
//自定义排序
void MySort(Node* p,int start,int end)//start表示排序起始位置,end表示排序结束位置
{
    for(int i=start;i<end-1;i++)
    {
        for(int j=start;j<end-(i-start)-1;j++)
        {

            if(p[j].val>p[j+1].val)
            {
                Node temp=p[j];
                p[j]=p[j+1];
                p[j+1]=temp;
            }
        }
    }
}
//根据生产的huffuman树生产每个字母的huffuman编码
void PreOrder(Node* root,string s)//这里是前序遍历
{
    if(root==NULL)
        return ;
    root->c=s;
    if(root->word!=' ')//只添加叶子节点也就是字母所在的节点
        strs[root->word-'a']=s;
    PreOrder(root->left,s+"0");//往左走则添加0
    PreOrder(root->right,s+"1");//往右走则添加1
}
string stringToHufuman(string words)//huffuman编码器
{
    string ans="";
    for(int i=0;i<words.length();i++)
        ans+=strs[words[i]-'a'];
    return ans;
}
string huffumanToString(string password,Node* root)//huffuman解码器,解码Huffuman编码
{
    string ans="";
    Node* head=root;
    for(int i=0;i<password.length();i++)
    {
        if(head->left==NULL&&head->right==NULL)//找到一个字母的Huffuman编码
        {
            ans+=head->word;
            head=root;//将head重置为root,开启下一轮查找
        }
        if(password[i]=='0')
            head=head->left;
        else
            head=head->right;
    }
    if(head->left==NULL&&head->right==NULL)//还需判断一下不然会漏掉最后一个字母
        ans+=head->word;
    return ans;
}
int main()
{
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        char word;
        int cnt;
        cin>>word>>cnt;
        nodes[i]={cnt,word,"",NULL,NULL};
    }
    for(int i=n;i<100;i++)
        nodes[i]={1000000000,' ',"",NULL,NULL};//将多余的节点val设置为一个很大的数就不会影响排序结果
    MySort(nodes,0,100);
    int idx=0;//设置排序的偏移量
    while(nodes[idx+1].val!=1e9)//至少还存在两个未合并的节点就一直循环
    {
        nodes[99].val=nodes[0+idx].val+nodes[1+idx].val;
        nodes[99].c=' ';
        nodes[99].left=&nodes[1+idx];//其次小的放在左子树
        nodes[99].right=&nodes[0+idx];//最小的放在右子树
        idx+=2;
        MySort(nodes,idx,100);
    }
    for(int i=0;i<26;i++)
        strs[i]="";
    Node* root=&nodes[idx];
    PreOrder(root,"");
    for(int i=0;i<26;i++)
    {
        if(strs[i]!="")
            cout<<char (i+'a')<<"的huffuman编码为:"<<strs[i]<<endl;
    }
    cout<<stringToHufuman("zhongnandaxue")<<endl;
    cout<<huffumanToString("00101101001100010001100101100110000011000111000001011010111010100",root)<<endl;
}
  • 2
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值