这是slidev中关于霍夫曼树的解析,再去b站找几个视频来看就很好理解了,简单来说就是叶子节点代表字符,根节点什么都不是
Part One: Build the Huffman Tree
这里用到了assignment6的最小堆,挺好理解的
EncodingTreeNode* huffmanTreeFor(const string& str) {
/* TODO: Delete this comment and the next few lines, then implement this. */
Map<char,int> hashmap;
for(char x:str){
hashmap[x]++;
}
if (hashmap.size()<=1){
error("Less than two char");
}
//直接存储 EncodingTreeNode 对象而不是指针,会导致对象被复制,而不是通过引用存储在队列中
PriorityQueue<EncodingTreeNode*> pq;
for(char key:hashmap){
EncodingTreeNode* newNode=new EncodingTreeNode;
newNode->ch=key;
newNode->zero=nullptr;
newNode->one=nullptr;
pq.enqueue(newNode,hashmap[key]);
}
while(pq.size()!=1){
int zeroPriority=pq.peekPriority();
EncodingTreeNode* zeroNode=pq.dequeue();
int onePriority=pq.peekPriority();
EncodingTreeNode* oneNode=pq.dequeue();
EncodingTreeNode* newCode=new EncodingTreeNode;
newCode->one=oneNode;
newCode->zero=zeroNode;
pq.enqueue(newCode,zeroPriority+onePriority);
}
return pq.dequeue();
}
Part Two: Implement Text Encoding and Decoding
思路也比较容易理解,在注释里 ,建议用笔在图上画一个树,然后让代码跟着纸上的逻辑来演示,笔者就是在这里出现逻辑错误,在纸上用代码的思路带入数据画一次就行
string decodeText(Queue<Bit>& bits, EncodingTreeNode* tree) {
/* TODO: Delete this comment and the next few lines, then implement this. */
//从bits中取出比特位,遍历tree,0往左走,1往右走,当tree为叶子节点时候,把字符添加到string
if(bits.size()==0){
return "";
}
EncodingTreeNode* Node=tree;
while(Node->one!=nullptr){
Bit bit=bits.dequeue();
if (bit==0){
Node=Node->zero;
}
else{
Node=Node->one;
}
}
//退出循环时Node已经是叶子节点
string res="";
char ch=Node->ch;
res+=ch;
res+=decodeText(bits,tree);
return res;
}
Queue<Bit> help(Queue<Bit> bits,char& ch,EncodingTreeNode* tree){
//当为叶子节点时,判断它的ch是否与传入的ch相等,相等返回队列,否则返回{}
if(tree->zero==nullptr && tree->one==nullptr){
if(tree->ch==ch){
return bits;
}
return {};
}
//zeroNode左节点,oneNode右节点
EncodingTreeNode* zeroNode=tree->zero;
EncodingTreeNode* oneNode=tree->one;
//当前根节点选择往zero分支走时,队列加元素0
Queue<Bit> zeroRoad;
zeroRoad=bits;
zeroRoad.enqueue(0);
zeroRoad=help(zeroRoad,ch,zeroNode);
//当前根节点选择往one分支走时,队列加元素1
Queue<Bit> oneRoad;
oneRoad=bits;
oneRoad.enqueue(1);
oneRoad=help(oneRoad,ch,oneNode);
//走得通返回正确队列,走不通返回{},我们只要返回正确队列就行
if(zeroRoad.size()!=0){
return zeroRoad;
}
return oneRoad;
}
Queue<Bit> encodeText(const string& str, EncodingTreeNode* tree) {
/* TODO: Delete this comment and the next few lines, then implement this. */
Queue<Bit> res;
string str1=str;
while(str1.size()!=0){
char ch=str1[0];
Queue<Bit> bits=help({},ch,tree);
while(bits.size()!=0){
Bit bit=bits.dequeue();
res.enqueue(bit);
}
str1=str1.substr(1);
}
return res;
}
Part Three: Implement Tree Encoding and Decoding
这部分较比上部分简单
EncodingTreeNode* decodeTree(Queue<Bit>& bits, Queue<char>& leaves) {
EncodingTreeNode* Node=new EncodingTreeNode;
//叶子节点时
if(bits.dequeue()==0){
Node->ch=leaves.dequeue();
Node->one=nullptr;
Node->zero=nullptr;
}
//树节点时(bits.dequeue()==1)
else{
Node->zero=decodeTree(bits,leaves);
Node->one=decodeTree(bits,leaves);
}
return Node;
}
void encodeTree(EncodingTreeNode* tree, Queue<Bit>& bits, Queue<char>& leaves) {
//叶子节点时
if(tree->one==nullptr && tree->zero==nullptr){
bits.enqueue(0);
leaves.enqueue(tree->ch);
}
//树节点时
else{
bits.enqueue(1);
//先对左节点处理,加入队列
encodeTree(tree->zero,bits,leaves);
//对右节点处理
encodeTree(tree->one,bits,leaves);
}
}
Part Four: Put It All Together
HuffmanResult compress(const string& text) {
/* TODO: Delete this comment and the next few lines, then implement this. */
auto tree = huffmanTreeFor(text);
Queue<Bit>Bits; Queue<char>Leaves; Queue<Bit>messageBits;
HuffmanResult res;
encodeTree(tree , Bits , Leaves);
messageBits = encodeText(text, tree);
deleteTree(tree);
res.messageBits = messageBits; res.treeBits = Bits; res.treeLeaves = Leaves;
return res;
}
string decompress(HuffmanResult& file) {
/* TODO: Delete this comment and the next few lines, then implement this. */
EncodingTreeNode* tree = decodeTree(file.treeBits, file.treeLeaves);
string str = decodeText(file.messageBits, tree);
deleteTree(tree);
return str;
}