05-树9 Huffman Codes (30 分)

05-树9 Huffman Codes (30 分)

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.

Input Specification:

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.

Output Specification:

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.

Sample Input:

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

Sample Output:

Yes
Yes
No
No

答案

#include <iostream>
#include <string>
using namespace std;
#define MaxNum 64

struct TreeNode{
    int Weight = 0;
    TreeNode *Left = nullptr;
    TreeNode *Right = nullptr;
};

struct HeapNode{ // 堆
    TreeNode Data[MaxNum];
    int Size = 0;
};

HeapNode *CreateHeap(){
    HeapNode *H = new(HeapNode);
    H->Data[0].Weight = -1; // 哨兵设置为-1
    return H;
}

// 最小堆的删除
TreeNode *DeleteMin(HeapNode *H){
    int Parent = 0, Child = 0;
    TreeNode temp;
    TreeNode *MinItem = new(TreeNode);
    *MinItem = H->Data[1]; // 保存树根

    temp = H->Data[(H->Size)--]; // 拿到最后的数据,并将总容量size-1

    for (Parent = 1; Parent * 2 <= H->Size ; Parent = Child) {
        Child = Parent * 2;
        // 左右儿子都在并且左儿子比右儿子大
        if (Child != H->Size && (H->Data[Child].Weight > H->Data[Child + 1].Weight)){
            Child ++;
        }
        // 如果尾权重比左右儿子最小的那个还小, 不用改变
        if(temp.Weight <= H->Data[Child].Weight) break;
        else H->Data[Parent] = H->Data[Child];
    }
    H->Data[Parent] = temp;
    return MinItem;
}

// 最小堆的插入
void Insert(HeapNode *H, TreeNode *item){
    int i = 0;
    i = ++(H->Size); // i 为当前总容量
    for (; item->Weight < H->Data[i/2].Weight; i = i / 2) {
        H->Data[i] = H->Data[i/2];
    }
    H->Data[i] = *item;
}

HeapNode *ReadData(int N, HeapNode *H, int A[]){
    char s;
    int value;
    for (int i = 0; i < N; ++i) {
        cin >> s;
        cin >> value;
        A[i] = value;
        TreeNode* T = new(TreeNode);
        T->Weight = value;
        Insert(H, T);
    }
    return H;
}
// 下面构造哈夫曼树的过程是一个由Heap变哈夫曼树的过程
TreeNode *Huffman(HeapNode *H){
    TreeNode *T = nullptr;
    int num = H->Size;
    // 两两合并,必定是进行n - 1次
    for (int i = 0; i < num - 1; ++i) {
        T = new(TreeNode);
        T->Left = DeleteMin(H);
        T->Right = DeleteMin(H);
        T->Weight = T->Left->Weight + T->Right->Weight;
        Insert(H, T);
    }
    T = DeleteMin(H);
    return T;
}

// 计算Huffman树的权重
int WPL(TreeNode *H, int n){
    if (!H->Right) return H->Weight * n;
    else return WPL(H->Left, n + 1) + WPL(H->Right, n + 1);
}

struct JNode{
    // 每个点非0即1 flag为1代表这里已经有一个前辈住进去了只要我完全走了前辈走的路,就重复出现
    int Flag = 0;
    JNode *Left = nullptr;
    JNode *Right = nullptr;
};

// 判断编码是否符合前缀码要求
bool Judge(string s, JNode *J){
    int i = 0;
    for (; i < s.length(); ++i) {
        if (s[i] == '0'){
            if (J->Left == nullptr){
                JNode *j = new(JNode);
                J->Left = j;
            }else{
                if (J->Left->Flag == 1){
                    return false;
                }
            }
            J = J->Left;
        }else{
            if (J->Right == nullptr){
                JNode *j = new(JNode);
                J->Right = j;
            }else{
                if (J->Right->Flag == 1){
                    return false;
                }
            }
            J = J->Right;
        }
    }
    J->Flag = 1;
    if (J->Left == nullptr && J->Right == nullptr){
        return true;
    }else return false;
}

int main(){
    int N = 0, n = 0;
    cin >> N;
    HeapNode *H = CreateHeap();
    int Value[MaxNum] = {};
    ReadData(N, H, Value);
    TreeNode *T = Huffman(H);
    int CodeLen = WPL(T, 0);
    cin >> n;

    string temp;
    char c;
    bool result;
    for (int i = 0; i < n; ++i) {
        int count = 0, flag = 0;
        JNode *J = new(JNode);
        for (int j = 0; j < N; ++j) {
            cin >> c >> temp;
            count += temp.length() * Value[j]; // 这就是Value的作用啊
            // 开始判断是否符合前缀码要求
            if (!flag){
                result = Judge(temp, J);
                if (!result) {
                    flag = 1;
                }
            }
        }
        delete J;
        if (result && count == CodeLen){
            cout << "Yes" << endl;
        }else{
            cout << "No" << endl;
        }
    }
    return 0;
}

遗留问题

main函数最后,flag部分存在的意义

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,837评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,551评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,417评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,448评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,524评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,554评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,569评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,316评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,766评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,077评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,240评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,912评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,560评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,176评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,425评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,114评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,114评论 2 352

推荐阅读更多精彩内容