数据结构慕课PTA 05-树9 Huffman Codes

题目内容

In 1953, David A. Huffman published his paper "A Method for the Construction of Minimum-Redundancy Codes", and hence printed his name in the history of computer science. As a professor who gives the final exam problem on Huffman codes, I am encountering a big problem: the Huffman codes are NOT unique. For example, given a string "aaaxuaxz", we can observe that the frequencies of the characters 'a', 'x', 'u' and 'z' are 4, 2, 1 and 1, respectively. We may either encode the symbols as {'a'=0, 'x'=10, 'u'=110, 'z'=111}, or in another way as {'a'=1, 'x'=01, 'u'=001, 'z'=000}, both compress the string into 14 bits. Another set of code can be given as {'a'=0, 'x'=11, 'u'=100, 'z'=101}, but {'a'=0, 'x'=01, 'u'=011, 'z'=001} is NOT correct since "aaaxuaxz" and "aazuaxax" can both be decoded from the code 00001011001001. The students are submitting all kinds of codes, and I need a computer program to help me determine which ones are correct and which ones are not.

输入格式

Each input file contains one test case. For each case, the first line gives an integer N (2≤N≤63), then followed by a line that contains all the N distinct characters and their frequencies in the following format:

c[1] f[1] c[2] f[2] ... c[N] f[N]
where c[i] is a character chosen from {'0' - '9', 'a' - 'z', 'A' - 'Z', '_'}, and f[i] is the frequency of c[i] and is an integer no more than 1000. The next line gives a positive integer M (≤1000), then followed by M student submissions. Each student submission consists of N lines, each in the format:
c[i] code[i]
where c[i] is the i-th character and code[i] is an non-empty string of no more than 63 '0's and '1's.

输出格式

For each test case, print in each line either "Yes" if the student's submission is correct, or "No" if not.
Note: The optimal solution is not necessarily generated by Huffman algorithm. Any prefix code with code length being optimal is considered correct.

输入样例(对应图1):

7
A 1 B 1 C 1 D 3 E 3 F 6 G 6
4
A 00000
B 00001
C 0001
D 001
E 01
F 10
G 11
A 01010
B 01011
C 0100
D 011
E 10
F 11
G 00
A 000
B 001
C 010
D 011
E 100
F 101
G 110
A 00000
B 00001
C 0001
D 001
E 00
F 10
G 11

输出样例

Yes
Yes
No
No

单词

redundancy

英 /rɪ'dʌnd(ə)nsɪ/ 美 /rɪ'dʌndənsi/

n. [计][数] 冗余(等于redundance);裁员;人浮于事

hence

英 /hens/ 美 /hɛns/
adv. 因此;今后

professor

英 /prə'fesə/ 美 /prə'fɛsɚ/

n. 教授;教师;公开表示信仰的人

respectively

英 /rɪ'spektɪvlɪ/ 美 /rɪ'spɛktɪvli/
adv. 分别地;各自地,独自地

compress

英 /kəm'pres/ 美 /kəm'prɛs/
vt. 压缩,压紧;精简

vi. 受压缩小

correct

英 /kə'rekt/ 美 /kə'rɛkt/
adj. (政治或思想)正确的;恰当的;端正的

v. 改正;批改(学生作业);校正;指出错误;抵消;校准(仪器);修正、调整(数据)

submit

英 /səb'mɪt/ 美 /səb'mɪt/

vi. 服从,顺从
vt. 使服从;主张;呈递;提交

distinct

英 /dɪ'stɪŋ(k)t/ 美 /dɪ'stɪŋkt/
adj. 明显的;独特的;清楚的;有区别的

submission

英 /səb'mɪʃ(ə)n/ 美 /səb'mɪʃən/

n. 投降;提交(物);服从;(向法官提出的)意见;谦恭

optimal

英 /'ɒptɪm(ə)l/ 美 /'ɑptəml/
adj. 最佳的;最理想的

prefix

英 /'priːfɪks/ 美 /'prifɪks/

n. 前缀
vt. 加前缀;将某事物加在前面

题目分析

本题算是树类题中最难的一道了,考察了二叉堆、哈夫曼树等问题的运用,其中必须要认识到编码最优解并不一定要由哈夫曼树构造得来,这要WPL的值为最优即可。
根据题目的提示,我们发现最优的字符编码方式需要满足:
1.首先WPL的值应该为最小值(与构造的哈夫曼树一致即可保证)。
2.要保证不会出现一个字符的编码是另一个字符的前缀(即保证要计算的结点都出现在叶子结点)。
要满足第一点,我们可以构造一个哈夫曼树,然后计算其WPL值(注意:虽然WPL的最小值不一定要考哈夫曼树构造,但是哈夫曼树构造的一定是WPL最小的情况),构造哈夫曼树,我们需要用到二叉堆,具体实现见下面的代码。
要满足第二点,我选择的方法是根据用户所给的编码自己构造一个树,如果编码为0且上一个结点没有左子树就malloc一个新节点(新节点一律把frequency设为0),如果有那么把左子树置为上一个结点。如果为1就看右子树,如此循环,结束时在最后一个结点把frequency赋给它(因为编码顺序是和输入时一致的,所以我构造了一个全局数组fre[],来依次存放frequency)。
当在编码路径上遇到frequency不为0的结点说明其遇到了叶子结点,这说明本次的编码一定出现了不满足第二点的情况。或者是一次编码路径结束后,最后一个结点的左右子树不全为空,这也说明了本次编码一定出现了前缀冲突的情况。

代码实现

#include
#include

typedef struct HFMTreeNode *Node;
typedef Node Root;
struct HFMTreeNode
{
    int frequency;
    Node lchild;
    Node rchild;
};//哈夫曼树结构

Node *MinHeap;
int HeapLength;
//最小堆

int M, N;
int *fre;//这个变量用来按顺序保存C[i]的frequency。

void create_heap(int maxsize);
void insert_heap(int pos, Node n);//因为要用DeleteMin来构造哈夫曼树,所以插入时要保存结点的左右子树。
Node DeleteMin_heap(void);
//最小堆操作

Root create_hfmtree(void);
int count_wpl(Root,int);
//哈夫曼树操作

bool is_prefix_true(Root);
//判断前缀是否正确

void create_heap(int maxsize)
{
    fre = (int*)malloc((maxsize - 1) * sizeof(int));
    MinHeap = (Node*)malloc(maxsize * sizeof(Node));
    HeapLength = 0;
    MinHeap[0] = (Node)malloc(sizeof(struct HFMTreeNode));//将首结点置为0,也可以置为一个负数,总之要保证其比所有的可能频率值小即可
    MinHeap[0]->frequency = 0;
    MinHeap[0]->lchild = NULL;
    MinHeap[0]->rchild = NULL;
    for (int i = 1; i < maxsize; i++)
    {
        char word;
        int frequency;
        getchar();//因为scanf接收空格,所以这里用一个getchar将其读取掉。
        scanf("%c %d", &word, &frequency);
        fre[i - 1] = frequency;
        Node n= (Node)malloc(sizeof(struct HFMTreeNode));
        n->frequency = frequency;
        n->lchild = NULL;
        n->rchild = NULL;
        insert_heap(i, n);
    }   
}

void insert_heap(int pos, Node n)
{
    while (n->frequency < MinHeap[pos / 2]->frequency)
    {
        MinHeap[pos] = MinHeap[pos / 2];
        pos /= 2;
    }
    MinHeap[pos] = n;
    HeapLength++;
}

Node DeleteMin_heap(void)
{
    Node temp=MinHeap[1];
    MinHeap[1] = MinHeap[HeapLength];
    HeapLength--;
    int parents = 1;
    int child = 2 * parents;
    while (child <= HeapLength)
    {
        if (child != HeapLength)
        {
            if (MinHeap[child]->frequency > MinHeap[child+1]->frequency)
                child++;
        }
        if (MinHeap[parents]->frequency > MinHeap[child]->frequency)
        {
            Node temp = MinHeap[parents];
            MinHeap[parents] = MinHeap[child];
            MinHeap[child] = temp;
        }
        else
            break;
        parents = child;
        child = 2 * parents;
    }
    return temp;
}
//最小堆操作

Root create_hfmtree(void)
{
    Node Root = NULL;
    scanf("%d", &N);
    create_heap(N + 1);
    while (1)
    {
        Node left = DeleteMin_heap();
        Node right = DeleteMin_heap();
        Node newnode = (Node)malloc(sizeof(struct HFMTreeNode));
        newnode->frequency = left->frequency + right->frequency;
        newnode->lchild = left;
        newnode->rchild = right;
        if (HeapLength != 0)
            insert_heap(HeapLength + 1, newnode);
        else
        {
            Root = newnode;
            break;
        }
    }
    return Root;
}

int count_wpl(Root t,int depth)
{
    if (t == NULL)
        return 0;
    else if (t->lchild == NULL && t->rchild == NULL)
        return t->frequency*depth;
    else
        return count_wpl(t->lchild, depth + 1) + count_wpl(t->rchild, depth + 1);
}
//哈夫曼树操作

bool is_prefix_true(Root t)
{
    int flag = 0;
    char word;
    char string[64];
    for (int i = 0; i < N; i++)
    {
        getchar();
        scanf("%c %s", &word, string);
        char *p = string;
        Node Last = t;
        while (*p != '\0')
        {
            if (*p == '0')
            {
                if (Last->lchild == NULL)
                {
                    Node Nnode = (Node)malloc(sizeof(struct HFMTreeNode));
                    Nnode->frequency = 0;
                    Nnode->lchild = NULL;
                    Nnode->rchild = NULL;
                    Last->lchild = Nnode;
                    Last = Nnode;
                }
                else
                {
                    if (Last->lchild->frequency != 0)
                        flag = 1;   
                    Last = Last->lchild;
                }
            }
            else if(*p == '1')
            {
                if (Last->rchild == NULL)
                {
                    Node Nnode = (Node)malloc(sizeof(struct HFMTreeNode));
                    Nnode->frequency = 0;
                    Nnode->lchild = NULL;
                    Nnode->rchild = NULL;
                    Last->rchild = Nnode;
                    Last = Nnode;
                }
                else
                {
                    if (Last->rchild->frequency != 0)
                        flag = 1;
                    Last = Last->rchild;
                }
            }
            p++;
        }
        if (Last->lchild != NULL || Last->rchild != NULL)
            flag = 1;
        else
            Last->frequency = fre[i];
    }
    if (flag == 1)
        return false;
    else
        return true;
}
//判断前缀是否正确

bool is_wpl_true(Root t1, Root t2)
{
    if (count_wpl(t1, 0) == count_wpl(t2, 0))
        return true;
    else
        return false;
}
//判断WPL是否一致

int main(void)
{
    Root t = create_hfmtree();
    scanf("%d", &M);
    for (int i = 0; i < M; i++)
    {
        Root check_t = (Node)malloc(sizeof(struct HFMTreeNode));
        check_t->frequency = 0;
        check_t->lchild = NULL;
        check_t->rchild = NULL;
        if (is_prefix_true(check_t))
        {
            if (is_wpl_true(t, check_t))
                printf("Yes\n");
            else
                printf("No\n");
        }
        else
            printf("No\n");
    }
}

转载于:https://www.cnblogs.com/z-y-k/p/11372249.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Huffman编码是一种用于数据压缩的算法,它通过将出现频率较高的字符用较短的编码表示,从而减少数据的存储空间。该算法的基本思想是构建一棵哈夫曼树,将字符的出现频率作为权值,然后从叶子节点开始向上遍历,将左子标记为,右子标记为1,最终得到每个字符的编码。哈夫曼编码具有唯一性,即每个字符都有唯一的编码,且任何一个编码都不是另一个编码的前缀。 ### 回答2: Huffman编码是一种压缩数据的方式。它使用的基本原理是将数据中频繁出现的字符使用较短的编码,而不常用的字符使用较长的编码,以达到压缩数据的目的。在Huffman编码中,我们使用二叉来表示每个字符的编码。左孩子被标记为0,右孩子被标记为1。当我们从根节点到叶子节点的路径上移动时,我们收集的所有0和1的序列将编码作为该字符的压缩表示。 具体来说,生成Huffman编码的过程包括以下步骤: 1. 统计给定数据集中每个字符出现的次数。 2. 将字符作为叶子节点构建二叉,每个叶子节点包含一个字符和该字符的频率。 3. 选择频率最小的两个节点,将它们作为左右子合并成一个新节点,其频率等于两个节点频率之和。 4. 将新节点插入二叉,并在每个节点添加一个标记为0或1的位。 5. 重复步骤3和步骤4,直到只剩下一个节点。 6. 通过遍历收集每个字符的Huffman编码。递归,并在每个节点处添加0或1,直到我们到达一个叶子节点。 Huffman编码的优点在于它可以使数据更紧凑,占用更少的存储空间。它也是在许多压缩和编码算法中广泛应用的基础。Huffman编码的缺点是在压缩小数据时,压缩效果可能不明显。这是因为压缩率受到输入数据的分布和大小的影响。在Huffman编码中,来自数据集的所有字符的比特序列可能具有不同的长度。因此,我们需要在压缩和解压缩时花费一些额外的时间来恢复原始数据。 总之,Huffman编码是一种有效的数据压缩算法,可以通过使用二叉来表示每个字符的编码来实现。它的主要优点是可以更紧凑地存储数据,但它仍然受到输入数据大小和分布的影响,并且在进行压缩和解压缩时需要花费额外的时间。 ### 回答3: 题目描述 Huffman code是一种贪心算法,用于编码数据,每个字符都对应一种可辨识的前缀二进制码,使得所有字符的编码总长度最短。给定n个权值作为n个叶子结点,构造一棵二叉,若该的带权路径长度达到最小,则称这样的二叉为最优二叉,也称为赫夫曼。 在赫夫曼中,每个叶子节点的权值就是原始数据中的权值,而非叶子节点不存储权值,比较特别的一种二叉。 输入格式 第1行: 一个正整数n(<=1000) 接下来n行: 每行一个正整数weight[i](weight[i]<=100000) 输出格式 共n-1行,为赫夫曼编码表,每个字符的赫夫曼编码占据一行。 样例输入1 5 1 3 2 10 5 样例输出1 0 110 111 10 11 样例输入2 5 23 3 6 16 8 样例输出2 100 0 101 1101 1100 解题思路 首先,将所有节点的权值从小到大排序。 接着构造一棵二叉: 每次从节点集合中选出最小的两个节点(即最小的两个权值) 将这两个点组成一棵新的二叉,其权值为这两个节点权值之和,这棵新的左右子即为这两个节点。 把这棵新加入到权值序列中,其位置按照新的权值插入,继续循环,直到权值序列只含有一个节点为止,这个节点就是赫夫曼的根。 最后,根据赫夫曼将每个叶子节点的编码求出来,一般情况下,将左子编码置“0”,右子编码置“1”,然后做前缀无歧义编码,按照这种编码方式,我们得到了每个节点的Huffman编码。 代码实现
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值