首页 > 其他分享 >Huffman实现

Huffman实现

时间:2023-04-14 14:33:06浏览次数:59  
标签:Node 编码 code 实现 data len root Huffman

Huffman编码树

秒懂:【算法】Huffman编码_哔哩哔哩_bilibili

约定:字符x的编码长度 就是其对应叶节点的深度;

在一个字符集中,每个字符出现的次数有多有少,那么若都采用固定长度编码的话,那么编码长度会非常大,并且搜索时间复杂度都非常高;若采用非固定编码,出现次数多的字符编码长度小一些,并且放在树深度小的地方,提高搜索时间效率;这样带权平均编码长度(weight average leaf depth)就会达到最优;同时为了避免歧义,任何字符不能是其他字符的编码前缀;

个人理解:

没有前缀:在具体实现时,由 priority_queue 排序完成后的 节点权值树 再转存在map中时,不会存储根节点,只会存叶子节点,就能避免前缀相同的情况;还有一点就是第一个设置为0,而不是1,1的话就会成为其他字符的前缀;

时间复杂度:对于出现次数多的字符,让它在靠近根节点位置,这样就能接近O(1)时间复杂度;而对于出现次数少的字符,就靠近树的最底部位置;

具体实现

代码参考:Canonical Huffman Coding - GeeksforGeeks

1、实现一个struct,保存字符出现的次数,以及字符本身,还有左右子节点;

2、写一个路径长度模块函数,create_code();

3、写一个Huffman编码函数,create_huffman(); 其中, 先按频率从小到大排列,然后取最小的两个合并为一大的,在继续合并直至成为一个根节点,再将字符树存进map中;接着实现Huffman编码;

  ①在编码时,利用路径长度信息,和bitset<32>类,实现位操作,并且利用成员函数to_string()转化为字符串,左边为高位,右边为低位;substr函数第二个参数长度设置为32,默认到头;

  ②怎么利用路径长度信息的? 比如说代码中 给出的例子c,编码为0,假设还有叶子节点,那么当前编码值加上1,然后再左移(下一层的深度 - 当前层的深度)位,即 0 +1 = 12,再左移(2-1)位,变成102;在内层循环中,当是同一层的最后两个叶子节点时,即110 和 111 , 不做左移,只做值加一操作,代码这儿用了一个next_len 和 cur_len 相减实现左移的次数值;(非常巧妙);

  ③学到了一个next函数,和bitset位操作;

#include <bits/stdc++.h>
using namespace std;

/*Huffman codes : a lossless data compression algorithm; weighted average leaf depth(带权平均深度最小)*/
struct Node{
    int data;
    char c;
    Node* left, *right;
};

struct mycomp{
    bool operator()(Node* a, Node* b){
        return a->data > b->data;
    }
};

class huffman{
private:
    map<int, set<char>> data;
public:
    huffman(){}
    void create_code(Node* root, int code_len){
        if(root == nullptr){
            return;
        }
        /*only store leaf node*/
        if(root->left == nullptr && root->right == nullptr){
            data[code_len].insert(root->c);
        }
        create_code(root->left, code_len + 1);
        create_code(root->right, code_len + 1);
    }
    void create_huffman(int n, char arr_char[], int freq[]){
        /* 小顶堆  取堆顶 freq 小的两个合并*/
        priority_queue<Node*, vector<Node*>, mycomp>que;
        for(int i = 0;i < n;++i){
            Node* newnode = new Node();
            newnode->c = arr_char[i];
            newnode->data = freq[i];
            newnode->left = nullptr;
            newnode->right = nullptr;
            que.push(newnode);
        }
        /*Node Tree*/
        Node* root = nullptr;
        while(que.size() > 1){
            Node* tmp1 = que.top();
            que.pop();
            Node* tmp2 = que.top();
            que.pop();
            Node* mergeNode = new Node();
            mergeNode->data = tmp1->data + tmp2->data;
            mergeNode->c = '-';
            mergeNode->left = tmp1;
            mergeNode->right = tmp2;
            root = mergeNode;
            que.push(mergeNode);
        }
        huffman obj = huffman();
        create_code(root, 0);
        int cur_code = 0, cur_len = 0, next_len = 0;
        for(map<int, set<char>>::iterator it = data.begin(); it != data.end(); ++it){
            set<char> s = it->second;
            cur_len = it->first;
            for(auto i = s.begin(); i != s.end(); ++i){
                cout << *i << " : ";
                /* coding */
                cout << bitset<32>(cur_code).to_string().substr(32 - cur_len, 32) << endl;
                
                /* 相同长度 有一个以上叶子节点时 : 这种情况只出现在 尾部的那俩个元素*/
                if(next(i) != s.end() || next(it) == data.end()) next_len = cur_len;
                else next_len = next(it)->first;
                
                cur_code = (cur_code + 1) << (next_len - cur_len);
            }
        }
    }
};

int main(){
    int n = 4;
    char arr[] = {'a', 'b', 'c', 'd'};
    int fre[] = {10, 1, 15, 7};
    huffman obj;
    obj.create_huffman(n,arr,fre); 
    system("pause");
    return 0;
}
/*
c : 0
a : 10
b : 110
d : 111
*/

 

标签:Node,编码,code,实现,data,len,root,Huffman
From: https://www.cnblogs.com/xuan01/p/17317584.html

相关文章

  • 【MySQL】主从复制实现原理详解
      目录一、概要1.1mysql复制的优点1.2Mysql复制解决的问题二、MySQL主从复制的概念三、MySQL主从复制的主要用途3.1读写分离3.2数据实时备份,当系统中某个节点发生故障时,可以方便的故障切换(主从切换)3.3高可用(HA)3.4架构扩展四、MySQL主从形式一主一从......
  • vue 使用 vue-seamless-scroll 实现LED屏滚动效果
    yarnaddvue-seamless-scroll<divclass="index-roll"><vue-seamless-scroll:data="partnerList":class-option="optionLeft"><ul><liv-for=&quo......
  • mac使用【预览】实现和win一样的【画图】功能
    1,打开预览软件,这个是mac自带的2,截屏3,全选删除截图4,打开工具栏按一下画笔一样的图标把工具栏显示出来就可以随便画了(或者选择菜单:显示-显示标记工具栏)。注意先调整一下画布尺寸,不然可花区域会比较小,然后就可以愉快的花各种图案了可以改颜色,添加字体,等,......
  • .NET7使用HttpClient实现查询天气预报接口
    朋友做网站需要根据城市展示天气预报,找了一圈没有找到靠谱的接口,今天在中央气象台的官网查询某个城市找到了接口,先用postman试了一下居然可以使用,可以查询某个城市7天的天气预报等信息。但是查询编码是气象台自己的编码,在网上搜索了一下居然有这个编码。本文使用HttpClient方法查......
  • 栈的定义及基本操作实现(顺序栈)
    个人主页:【......
  • 【Unity/大气渲染】单次散射的原理和简单实现
    这篇随笔将会简单实现一个基于物理的相对真实的大气渲染效果如下图,太空中的星球、相对真实的天空盒如果没有大气,太阳光没有直接照射到的地方将会是一片黑暗而我们能从太空中看到星球表面泛起的蓝光,日出时的美丽景色,都得于太阳光在大气中的散射地球的大气中充斥着诸如空气分子,......
  • 动态规划——经典问题的实现(Python)
    动态规划(dunamicprogramming,DP)是运筹学的一个分支,是求解决策过程最优化的过程。将复杂的多段决策问题分解为若干相互关联的子决策问题以获得最优决策序列的方法,是由美国数学家贝尔曼(R.E.Bellman)于20世纪50年代提出,它的基本原理是贝尔曼在《动态规划》(1957年)一书中所提出的最......
  • 用Abp实现找回密码和密码强制过期策略
    @目录重置密码找回密码发送验证码校验验证码发送重置密码链接创建接口密码强制过期策略改写接口Vue网页端开发重置密码页面忘记密码控件密码过期提示项目地址用户找回密码,确切地说是重置密码,为了保证用户账号安全,原始密码将不再以明文的方式找回,而是通过短信或者邮件的方式发送一......
  • 如何实现一个vscode插件
    前言有时候,需要提高一些开发效率,我们通常会使用一些优秀的代码编辑器,比如vscode。在使用vscode的时候,会用到很多插件,有时候也会萌发想要去开发这个插件的念头。既然想到了,那就动手试一下。开发过程我感觉最快的上手方式不是讲一些虚头巴脑的概念,先去试一下怎么去实现一个简......
  • Midjourney魔法解锁:打造电商AI模特,实现无限场景换装
    在网上看到过下图这样一篇《模特不存在了》的帖子:是一个卖内衣的店主,通过Midjourney把石膏模特身上的衣服,穿到了AI生成的模特身上。网上看到的把石膏模特的内衣穿在了AI模特身上可以看到这张图片上左侧的衣服,几乎无差别的穿到了AI模特的身上。但这个帖子没有公布方法和技巧,那......