05-树10 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 NN (2\le N\le 632N63), then followed by a line that contains all the NNdistinct 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', '_'}, andf[i] is the frequency of c[i] and is an integer no more than 1000. The next line gives a positive integer MM (\le 10001000), then followed by MM student submissions. Each student submission consists of NN 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:

<script src="https://gist.github.com/Talentwill/f044055edc41e5fd0320e15e9034ceb0.js"></script>

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


注意:

哈夫曼树要求树没有度为1的结点,这里题目要求编码可以有度为1的结点,只要编码最优即可。

结构设计:

根据输入,自己构建一棵哈夫曼树,有N个元素,所以所有结点数量是2N-1,把每个结点都存在数组里,前N个结点就是叶子结点,后N-1个存合并出来的结点。

判断正确依据:
  1. WPL值相同
  2. 前缀码不相同
求WPL:
  • 从叶子结点开始依次访问父结点即可。
  • 学生编码的WPL很简单,直接编码长度*权值。
前缀码:
  • 字符串匹配,如果匹配到位置为0,就说明有前缀相同。
#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
        
        
using namespace std;

class HuffmanTree {
    struct Node {
        Node(int w = 0) :weight(w), left(NULL), right(NULL), parent(NULL) {}
        int weight;
        Node* left;
        Node* right;
        Node* parent;
    };

    class MinHeap {
    public:
        MinHeap(int N) {
            cbtree = new Node*[N + 1];
            //set samllest guard
            cbtree[0] = new Node(-1);
            size = 0;
        };

        ~MinHeap() {
            delete cbtree[0];
            cbtree[0] = NULL;
            delete[] cbtree;
        }
        int Size() {
            return size;
        }
        void Insert(Node* item) {
            int i = ++size;
            for (; item->weight < cbtree[i / 2]->weight; i = i / 2) {
                cbtree[i] = cbtree[i / 2];
            }
            cbtree[i] = item;
            return;
        }

        Node* DeleteMin() {

            Node* min_node = cbtree[1]; // the minimun item
            Node* last_node = cbtree[size];  // the last item
            cbtree[size--] = NULL; //clear the last pointer

            int child, parant = 1;
            // loop until parant has no children
            while (parant * 2 <= size) {
                child = 2 * parant;
                // has right child && get the minimun of them
                if (child != size && cbtree[child + 1]->weight < cbtree[child]->weight) {
                    child++;
                }

                if (last_node->weight < cbtree[child]->weight) {
                    // item is less than its child, Ok
                    break;
                } else {
                    // child is less then parent, move to parent
                    cbtree[parant] = cbtree[child];
                }
                parant = child;
            }
            cbtree[parant] = last_node;
            return min_node;
        }

    private:
        Node* *cbtree;
        int size;
    };
public:
    HuffmanTree(int capacity) :heap(capacity), root(NULL), leaves(0), wpl(0) {

    }

    ~HuffmanTree() {
        for (size_t i = 0; i < nodes.size(); i++) {
            delete nodes[i];
        }
    }

    void Insert(int item) {
        leaves++;
        nodes.push_back(new Node(item));
        heap.Insert(nodes.back());
    }

    int GetWeight(int leave) {
        return nodes[leave]->weight;
    }

    int GetWPL() {
        if (!root) {
            Build();
        }
        if (wpl == 0) {
            for (int i = 0; i < leaves; i++) {
                int depth = 0;
                for (Node* leave = nodes[i]; leave->parent; leave = leave->parent) {
                    depth++;
                }
                wpl += depth * nodes[i]->weight;
            }
        }
        return wpl;
    }
private:

    Node* NextEmptyNode() {
        static int next = leaves;
        next++;
        nodes[next] = new Node();
        return nodes[next];
    }
    void Build() {
        if (!root) {
            nodes.resize(2 * leaves - 1);
            for (int i = 0; i < leaves - 1; i++) {
                Node* merged = NextEmptyNode();
                merged->left = heap.DeleteMin();
                merged->right = heap.DeleteMin();
                merged->weight = merged->right->weight + merged->right->weight;
                merged->right->parent = merged->left->parent = merged;
                heap.Insert(merged);
            }
            root = heap.DeleteMin();
        }
    }

private:
    MinHeap heap;
    Node* root;
    int leaves;
    int wpl;
    vector
        
        
         
          nodes;
};

bool IsPrefixConflict(string codes[], int N) {
    for (size_t i = 0; i < N; i++) {
        for (size_t j = 0; j != i && j < N; j++) {
            if (codes[j].find(codes[i]) == 0) {
                return true;
            }
        }
    }
    return false;
}

int main() {

    int N = 0;
    cin >> N;

    char c; int f;
    HuffmanTree tree(N);
    for (int i = 0; i < N; i++) {
        cin >> c >> f;
        tree.Insert(f);
    }

    int M = 0;
    cin >> M;
    for (size_t mstu = 0; mstu < M; mstu++) {
        // save all the input codes
        string codes[64];
        for (size_t ncode = 0; ncode < N; ncode++) {
            cin >> c >> codes[ncode];
        }

        // calculate the wpl of the codes
        int wpl = 0;
        for (int kcode = 0; kcode < N; kcode++) {
            wpl += (codes[kcode].length() * tree.GetWeight(kcode));
        }

        // check prefix error and both wpl
        if (!IsPrefixConflict(codes, N) && wpl == tree.GetWPL()) {
            printf("Yes\n");
        } else {
            printf("No\n");
        }
    }

    return 0;
}emptyemptyemptyemptyemptyemptyemptyempty
        
        
       
       
      
      
     
     
<script src="https://gist.github.com/Talentwill/f044055edc41e5fd0320e15e9034ceb0.js"></script>

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值