DS二叉树——Huffman编码与解码(不含代码框架)

题目
问题 H: DS二叉树——Huffman编码与解码(不含代码框架)
时间限制: 1 Sec  内存限制: 128 MB
提交: 475  解决: 241
[提交][状态][讨论版]
题目描述
1、问题描述

给定n个字符及其对应的权值,构造Huffman树,并进行huffman编码和译(解)码。

构造Huffman树时,要求左子树根的权值小于、等于右子树根的权值。

进行Huffman编码时,假定Huffman树的左分支上编码为‘0’,右分支上编码为‘1’。

2、算法

构造Huffman树算法:

⑴ 根据给定的n个权值(w1, w2,, wn)构成n棵二叉树的集合F={T1, T2,, Tn},其中每棵二叉树Ti中只有一个权值为wi的根结点。

⑵ 在F中选取两棵根结点的权值最小的树,作为左、右子树构造一棵新的二叉树,且置其根结点的权值为其左、右子树权值之和。

⑶ 在F中删除这两棵树,同时将新得到的二叉树加入F中。

(4) 重复⑵, ⑶,直到F只含一棵树为止。

3、Huffman编码算法:

⑴ 从Huffman树的每一个叶子结点开始。

⑵ 依次沿结点到根的路径,判断该结点是父亲结点的左孩子还是右孩子,如果是左孩子则得到编码‘0’,否则得到编码‘1’,先得到的编码放在后面。

⑶ 直到到达根结点,编码序列即为该叶子结点对应的Huffman编码。

4、Huffman译(解)码算法:

⑴ 指针指向Huffman树的根结点,取第一个Huffman码。

⑵ 如果Huffman码为‘0’,将指针指向当前结点的左子树的根结点;如果Huffman码为‘1’,将指针指向当前结点的右子树的根结点。

⑶ 如果指针指向的当前结点为叶子结点,则输出叶子结点对应的字符;否则,取下一个Huffman码,并返回⑵。

⑷ 如果Huffman码序列未结束,则返回⑴继续译码。

输入
第一行测试次数

第2行:第一组测试数据的字符个数n,后跟n个字符

第3行:第一组测试数据的字符权重

待编码的字符串s1

编码串s2

其它组测试数据类推

输出
第一行~第n行,第一组测试数据各字符编码值

第n+1行,串s1的编码值

第n+2行,串s2的解码值,若解码不成功,输出error!

其它组测试数据类推

样例输入
2
5 A B C D E
15 4 4 3 2
ABDEC
00000101100
4 A B C D
7 5 2 4
ABAD
1110110

样例输出
A :1
B :010
C :011
D :001
E :000
1010001000011
error!
A :0
B :10
C :110
D :111
0100111
DAC
代码块
#include <iostream>
#include <string>
using namespace std;

class HTNode
{//这里虽然是树类结点,但是在整个huffman编码中并没有构建树,而是构建具有树意义的数组,但是其又和二叉树的数组存储不同,数组存储通过从下标i到2*i和2*i+1实现从父到子,而huffman的数组单元是结构体,其中设置了parent和lchild、rchild成员,其通过这些下标“指针”实现父到子或子到父。
private:
    int weight;
    int parent;
    int lchild;
    int rchild;
public:
    HTNode():weight(0),parent(0),lchild(0),rchild(0){};//结点成员全都初始化为0.
    ~HTNode(){};
    friend class HuffmanTree;
};

class HuffmanTree
{
private:
    int LeafNum;//字符的数目
    HTNode *HT;//存Huffman树的数组,申请2*leafnum个空间,其中1到2*leafnum-1装结点(1到leafnum装叶子结点,即代表字符的结点),0号单元未用,且2*leafnum-1处一定是huffman树的根节点。
    char *str1;//存各字符
    int *str2;//存各字符权值
    string *HC;//存各字符的Huffman编码
    void Select(int pos, int *s1, int *s2);//选parent为0且权值最小的两个节点
public:
    HuffmanTree();
    ~HuffmanTree();
    void CreateTree();//建成Huffman树的数组HT
    void HuffmanCoding();//从叶子到根求各字符的Huffman编码
    void EnCoding(string str);//编码
    void DeCoding(string str);//译码
};

HuffmanTree::HuffmanTree()
{
    int i;
    cin>>LeafNum;
    HC = new string[LeafNum];
    str1 = new char[LeafNum];
    for(i=0; i<LeafNum; i++)
        cin>>str1[i];
    str2 = new int[LeafNum];
    for(i=0; i<LeafNum; i++)
        cin>>str2[i];
    HT = new HTNode[2*LeafNum];
    for(int i=1; i<=LeafNum; i++)
        HT[i].weight = str2[i-1];//HT数组的1到leafnum按顺序代表输入的字符str1
}

HuffmanTree::~HuffmanTree()
{
    delete []HC;
    delete []str1;
    delete []str2;
    delete []HT;
    LeafNum = 0;
    HT = NULL, HC = NULL, str1 = NULL, str2 = NULL;
}

void HuffmanTree::Select(int pos, int *s1, int *s2)
{//选parent为0且权值最小的两个节点
    int i;
    int w1 = 99999, w2 = 99999;
    for(i=1; i<=pos; i++)
    {
        if(HT[i].parent==0)
        {
            if(HT[i].weight<w1)
            {
                w1 = HT[i].weight;
                *s1 = i;
            }
        }
    }
    for(i=1; i<=pos; i++)
    {
        if(HT[i].parent==0 && i!=*s1)
        {
            if(HT[i].weight<w2)
            {
                w2 = HT[i].weight;
                *s2 = i;
            }
        }
    }
}

void HuffmanTree::CreateTree()
{
    for(int i=LeafNum+1; i<2*LeafNum; i++)
    {
        int s1, s2;
        Select(i-1, &s1, &s2);//每次只对前i-1个进行select
        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;//非叶子结点(不代表字符的结点)的权值为其select出来的两孩子的权值之和
    }
}

void HuffmanTree::HuffmanCoding()
{//从叶子出发到根逆向求每个字符的huffman编码
    for(int i=0; i<LeafNum; i++)
    {
        int m = i+1;
        int n = HT[m].parent;
        while(n!=0)
        {
            if(HT[n].lchild==m)
                HC[i] = '0'+HC[i];//string的左加字符操作
            else
                HC[i] = '1'+HC[i];
            m = n;
            n = HT[m].parent;
        }
        cout<<str1[i]<<" :"<<HC[i]<<endl;
    }
}

void HuffmanTree::EnCoding(string str)
{
    int len = str.length();
    for(int i=0; i<len; i++)
    {
        for(int j=0; j<LeafNum; j++)
        {
            if(str1[j]==str[i])
                cout<<HC[j];
        }
    }
    cout<<endl;
}

void HuffmanTree::DeCoding(string str)
{//解码不是根据huffman编码来,而是根据huffman树来,根据密码的0和1从huffman树的根出发向左或向右,如果到叶子了,则一次解码结束,叶子的内容即为解码的值。
    int len = str.length();
    int pos = 2*LeafNum-1;
    string temp;
    for(int i=0; i<len; i++)
    {
        if(str[i]=='0')
            pos = HT[pos].lchild;
        else
            pos = HT[pos].rchild;
        if(pos<=LeafNum)
        {
            temp += str1[pos-1];//string的直接加字符操作
            pos = 2*LeafNum-1;
        }
    }
    if(pos!=2*LeafNum-1)
        cout<<"error!"<<endl;
    else
        cout<<temp<<endl;
}

int main(void)
{
    int t;
    cin>>t;
    while(t--)
    {
        HuffmanTree myTree;
        myTree.CreateTree();
        myTree.HuffmanCoding();
        string str;
        cin>>str;
        myTree.EnCoding(str);
        cin>>str;
        myTree.DeCoding(str);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值