如何用C ++表示二进制数(用于霍夫曼编码器)?

时间:2013-07-28 03:16:33

标签: c++ binary-tree bit-manipulation huffman-code tree-traversal

我正在编写自己的Huffman encoder,到目前为止,我已经创建了Huffman树,使用minHeap弹出两个最低频率节点并创建一个链接到它们的节点然后再推回新节点一个(泡沫,冲洗,重复,直到只有一个节点)。

所以现在我已经创建了树,但是我需要使用这个树为每个字符分配代码。我的问题是我不知道如何在C ++中存储数字的二进制表示。我记得读过unsigned char是一个字节的标准,但我不确定。

我知道我必须反复遍历树,每当我点击一个叶子节点时,我必须分配相应的字符,无论当前代表路径的代码是什么。

这是我到目前为止所做的:

void traverseFullTree(huffmanNode* root, unsigned char curCode, unsigned char &codeBook){

    if(root->leftChild == 0 && root->rightChild == 0){ //you are at a leaf node, assign curCode to root's character
        codeBook[(int)root->character] = curCode;
    }else{ //root has children, recurse into them with the currentCodes updated for right and left branch
        traverseFullTree(root->leftChild, **CURRENT CODE SHIFTED WITH A 0**, codeBook );
        traverseFullTree(root->rightChild, **CURRENT CODE SHIFTED WITH A 1**, codeBook);
    }

    return 0;
}

CodeBook是我的数组,其中包含最多256个字符的代码(对于ASCII中的每个可能字符),但我只是实际将代码分配给树中出现的值。

我不确定这是否是穿越我的霍夫曼树的正确方法,但这是立刻似乎工作的东西(虽然我还没有测试过)。另外如何调用整个树的根的遍历函数,没有零或者(树的最顶部)?

我是否应该使用字符串并将字符串附加到零或1?

4 个答案:

答案 0 :(得分:1)

由于计算机是二进制的...... C / C ++中的所有数字都是二进制格式。

int a = 10;

变量a是二进制数。

您要看的是位操作,& | << >>等操作符。

使用霍夫曼编码,您可以将数据打包成一个字节数组。

我写C以来已经很久了,所以这是一个“袖手旁观”的伪代码......

完全未经测试 - 但应该给你正确的想法。

char buffer[1000]; // This is the buffer we are writing to -- calc the size out ahead of time or build it dynamically as go with malloc/ remalloc.

void set_bit(bit_position) {
  int byte = bit_position / 8;
  int bit = bit_position % 8;

  // From http://stackoverflow.com/questions/47981/how-do-you-set-clear-and-toggle-a-single-bit-in-c
  byte |= 1 << bit;
}

void clear_bit(bit_position) {
  int byte = bit_position / 8;
  int bit = bit_position % 8;

  // From http://stackoverflow.com/questions/47981/how-do-you-set-clear-and-toggle-a-single-bit-in-c
 bite &= ~(1 << bit);
}


// and in your loop, you'd just call these functions to set the bit number.
set_bit(0);
clear_bit(1);

答案 1 :(得分:0)

由于curCode只有0和1作为其值,BitSet可能适合您的需要。它方便且节省内存。参考:http://www.sgi.com/tech/stl/bitset.html

只对您的代码稍作修改:

void traverseFullTree(huffmanNode* root, unsigned char curCode, BitSet<N> &codeBook){

    if(root->leftChild == 0 && root->rightChild == 0){ //you are at a leaf node, assign curCode to root's character
        codeBook[(int)root->character] = curCode;
    }else{ //root has children, recurse into them with the currentCodes updated for right and left branch
        traverseFullTree(root->leftChild, **CURRENT CODE SHIFTED WITH A 0**, codeBook );
        traverseFullTree(root->rightChild, **CURRENT CODE SHIFTED WITH A 1**, codeBook);
    }

    return 0;
}

答案 2 :(得分:0)

请不要使用字符串。

您可以将码本表示为两个整数数组,一个包含代码的位长度,另一个包含代码本身。有一个问题:如果代码长于整数怎么办?解决方案是不要让这种情况发生。由于各种原因,具有短的最大码长(比如15)是霍夫曼编码的大多数实际用途中使用的技巧。

我建议使用canonical Huffman codes,这会略微简化树遍历:您只需要长度,因此您无需跟踪当前代码。使用规范的霍夫曼代码,您可以轻松地从长度生成代码。

如果您使用规范代码,则可以让代码比整数更宽,因为无论如何高位都是零。但是,限制长度仍然是个好主意。具有较短的最大长度(不是短,这将限制压缩,但大约16)使您能够使用最简单的table-based decoding方法,一个简单的单级表。

将代码长度限制为25或更小也可以略微简化编码,它允许您使用32位整数作为“缓冲区”并逐字节清空,而无需对缓冲区保持少于8位的情况进行任何特殊处理编码当前符号会使其溢出(因为完全避免了这种情况 - 在最坏的情况下,缓冲区中会有7位,并且您尝试编码25位符号,这样可以正常工作)。

像这样的东西(没有以任何方式测试)

uint32_t buffer = 0;
int bufbits = 0;
for (int i = 0; i < symbolCount; i++)
{
    int s = symbols[i];
    buffer <<= lengths[s];  // make room for the bits
    bufbits += lengths[s];  // buffer got longer
    buffer |= values[s];    // put in the bits corresponding to the symbol

    while (bufbits >= 8)    // as long as there is at least a byte in the buffer
    {
        bufbits -= 8;       // forget it's there
        writeByte((buffer >> bufbits) & 0xFF); // and save it
    }
}

答案 3 :(得分:0)

  

如何在C ++中存储数字的二进制表示

您只需使用bitsets

即可
#include <iostream>
#include <bitset>

int main() {
  int a = 42;
  std::bitset<(sizeof(int) * 8)> bs(a);

  std::cout << bs.to_string() << "\n";
  std::cout << bs.to_ulong() << "\n";
  return (0);
}

正如您所看到的,它们还提供了转换为其他类型的方法,以及方便的[]运算符。