贪心算法(Greedy Algorithm)之霍夫曼编码

文章目录

    • 1. 贪心算法
    • 2. 应用
      • 2.1 找零钱
      • 2.2 区间覆盖
      • 2.3 霍夫曼编码
        • 霍夫曼编码完整代码

1. 贪心算法

  • 我们希望在一定的限制条件下,获得一个最优解
  • 每次都在当前的标准下做出当下最优决策(整体不一定最优),做出的决策不可以后悔,即不回溯,最终可以得到较为满意的解
  • 贪心算法不追求最优解,节省时间,避免穷尽所有可能

2. 应用

2.1 找零钱

给定金额的找零,用最少张(枚)钱给顾客。(总是优先给大额的)

/*** @description: 贪心应用--找零钱* @author: michael ming* @date: 2019/6/28 22:02* @modified by: */
#include <iostream>
#include <memory.h>
#include <math.h>
#define N 10
using namespace std;
void exchange(float money, float *rmb, int *amount)
{if(money < 0.1)return;int i, k = 0;for(i = 0; i < N; ++i){money = round(money*10)/10.0;//四舍五入掉分k = money/rmb[i];amount[i] = k;money = money-k*rmb[i];}
}
int main()
{float rmb[N] = {100, 50, 20, 10, 5, 2, 1, 0.5, 0.2, 0.1};int amount[N];while(1){memset(amount,0,N*sizeof(int));float money;cout << "请输入要找的钱的金额:";cin >> money;money = round(money*10)/10.0;//四舍五入掉分cout << "找零结果如下(分位四舍五入):" << endl;exchange(money,rmb,amount);int i = 0;while(i < N){if(amount[i] != 0)cout << amount[i] << "个" << rmb[i] << " ";i++;}cout << endl;cout << "----------------------" << endl;}
}

在这里插入图片描述

2.2 区间覆盖

运用场景:任务调度,教师排课,学生活动室安排等
在这里插入图片描述
在上面图中再加入些区间数据[2,3];[-1,4],[5,12];[4,5],代码实现如下:

/*** @description: 贪心算法--区间覆盖应用* (给定每个人可以在一个房间内活动的时间,要求让最多的人在这个房间活动,打印出这些人)* @author: michael ming* @date: 2019/6/29 23:34* @modified by: */
#include <iostream>
#include <algorithm>
using namespace std;
struct Interval
{int left, right;bool operator<(const Interval &sel_idx) const{if(left == sel_idx.left)return right < sel_idx.right;//左端点相等,取右端小的elsereturn left < sel_idx.left;//左端不等,取左端小的}bool belongto(int l, int r)//区间属于[l,r]的子集{return left >= l && right <= r;}bool absnotbelong(int l, int r)//两个区间完全无重叠{return left >= r || right <= l;}
};
int main()
{const int N = 10;//区间数量int left = 1, right = 10;//大区间左右端点(房间开放时间)int select[N];//存储选出来的区间(人)idint i, j, k, sel_idx = 0;for(i = 0; i < N; ++i){select[i] = -1;}Interval qujian[N] = {{1,5},{2,4},{3,5},{5,9},{6,8},{8,10},{2,3},{-1,4},{5,12},{4,5}};//所有人的占用时间段sort(qujian,qujian+N);//对区间进行排序(先按开始时间早排序,一样早,按占用时间少排前面)for(i = 0; i < N; ++i){if(qujian[i].left >= left && qujian[i].right <= right)//占用时间在开放时间内{while(sel_idx != 0 && !qujian[i].absnotbelong(qujian[select[sel_idx-1]].left,qujian[select[sel_idx-1]].right)){//如果有人占用了房间,一直找到第一个跟他时间不冲突的人++i;}for(k = i, j = k+1; j < N; ++j){//找到时间是不冲突那个人的最小子集的人if(qujian[j].belongto(qujian[k].left,qujian[k].right)){k = j;}}select[sel_idx++] = k;//占用最短的那个人选出来i = k;//从这个人开始再往后找}}cout << "total selected " << sel_idx << " people, their time as following:" << endl;for(i = 0; i < N && select[i] != -1; ++i){//打印被选出来的人的时间信息cout << i << ": [" << qujian[select[i]].left << ","<< qujian[select[i]].right << "]" << endl;}return 0;
}

在这里插入图片描述

2.3 霍夫曼编码

  • 假设有一个包含1000个字符的文件,每个字符占1个byte(1byte=8bits),存储这1000个字符一共需要8000bits,有没有更加节省空间的存储方式呢?
  • 假设通过统计发现,这1000个字符中只包含6种不同字符,假设它们分别是a、b、c、d、e、f。而3个二进制位(bit)就可以表示8个不同的字符,a(000)、b(001)、c(010)、d(011)、e(100)、f(101),所以,为了尽量减少存储空间,每个字符我们用3个二进制位来表示。那存储这1000个字符只需要3000bits就可以了,比原来的存储方式节省了很多空间。
  • 还有没有更加节省空间的存储方式呢?
  • 霍夫曼编码,考虑字符的出现频率,频率小的,用长编码,大的,用短编码,使得总体编码长度变短(且由于其编码方式,没有一个字符的编码是另一个的编码的前缀,避免了解码过程中的歧义)
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

霍夫曼编码完整代码

/*** @description: 贪心应用--霍夫曼编码* @author: michael ming* @date: 2019/6/30 23:53* @modified by: */
#include <string>
#include <vector>
#include <queue>
#include <iostream>
#include <algorithm>
#include <memory.h>#define N 6		//字符集字符种数
using namespace std;
struct htNode//霍夫曼树节点
{char data;//数据类型char code;//数据存放的节点编码unsigned int weight;//数据权值htNode *parent, *lchild, *rchild;//连接节点指针htNode():data('/'),code('\0'),weight(0){parent = lchild = rchild = NULL;}
};
class comp//优先队列比较函数
{
public:bool operator()(htNode* &a, htNode* &b)const{if(a->weight == b->weight)return a->data > b->data;return a->weight > b->weight;}
};class HuffmanTree
{
public:htNode *root;//根节点指针htNode* node[2*N-1];//N个字符,霍夫曼树节点个数2*N-1priority_queue<htNode*,vector<htNode*>,comp> pri_queue;//优先队列中存放类指针时,第三个参数应该另写一个comp类,类内写operator()void creatTree_outputCode(int *w){char ch = 'a';htNode *left, *right;for(int i = 0; i < N; ++i,++ch){//生成前N个字符节点,输入权重,和字符信息//并放入优先队列(权值小的优先)node[i] = new htNode();node[i]->weight = w[i];node[i]->data = ch;pri_queue.push(node[i]);}for(int i = N; i < 2*N-1; ++i){//后面新生成的N-1个节点node[i] = new htNode();if(pri_queue.top()->data != '/'){//队首的节点不是新生成的,队首放右边right = pri_queue.top();right->code = '1';//右边节点编码1pri_queue.pop();left = pri_queue.top();left->code = '0';//左边节点编码0pri_queue.pop();//左右节点出队}else{//队首是新生成的节点,放左边//(以上if-else保证新生成的节点总在左边)left = pri_queue.top();left->code = '0';pri_queue.pop();right = pri_queue.top();right->code = '1';pri_queue.pop();//左右节点出队}//新节点权值、上下连接指针对接node[i]->weight = left->weight+right->weight;node[i]->lchild = left;node[i]->rchild = right;left->parent = node[i];right->parent = node[i];pri_queue.push(node[i]);//新生成的节点入队}root = pri_queue.top();//最后还剩一个节点,是根节点creatHuffCode();for(int i = 0; i < 2*N-1; ++i)//释放资源{delete node[i];}}void creatHuffCode(){htNode *parent;string huffcode;//霍夫曼编码int codelen = 0;//输入的字符串编码后的总长度bitsfor(int i = 0; i < N; ++i)//遍历前N个字符节点,求其编码{huffcode = "";parent = node[i];//从自己(叶子节点)开始向上找父节点,直到rootcout << i+1 << " " << node[i]->data << " 的霍夫曼编码是: ";while(parent != root)//{huffcode.push_back(parent->code);//将路径中的编码汇成字符串parent = parent->parent;}reverse(huffcode.begin(),huffcode.end());//将最终的编码反转一下cout << huffcode << endl;codelen += huffcode.size()*node[i]->weight;//单字符code长*出现次数}cout << "该字符串的huffman编码长度为: " << codelen << " bits.";}
};int main()
{HuffmanTree huff;cout << "请输入某字符串中" << N << "个字母abc...的权值(频率):" << endl;int w[N];//权重for(int i = 0; i < N; ++i){cout << i+1 << " ";cin >> w[i];//输入权值}huff.creatTree_outputCode(w);//将权值传入并生成Huffman树;生成霍夫曼编码,打印出来return 0;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/480171.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构中基本查找算法总结

原文地址&#xff1a;https://www.cnblogs.com/xuzhp/p/4638937.html 基本查找算法 一、查找的基本概念 查找&#xff0c;也可称检索&#xff0c;是在大量的数据元素中找到某个特定的数据元素而进行的工作。查找是一种操作。 二、顺序查找 针对无序序列的一种最简单的查找方式…

领域应用 | 大众点评搜索基于知识图谱的深度学习排序实践

本文转载自公众号&#xff1a;美团技术团队。 本文介绍了大众点评搜索核心排序层模型的演化之路&#xff0c;包括结合知识图谱信息构建适合搜索场景的Listwise深度学习排序模型LambdaDNN以及特征工程实践和相关工具建设。1. 引言挑战与思路搜索是大众点评App上用户进行信息查…

KDD2020 | 揭秘Facebook搜索中的语义检索技术

星标/置顶小屋&#xff0c;带你解锁最萌最前沿的NLP、搜索与推荐技术文 | 江城编 | 可盐可甜兔子酱导读&#xff1a;今天分享一下 Facebook 发表在 KDD2020 的一篇关于社交网络搜索中的 embedding 检索问题的工作&#xff0c;干货很多&#xff0c;推荐一读。论文题目&#xff1…

Redis系列教程(八):分布式锁的由来、及Redis分布式锁的实现详解

在很多场景中&#xff0c;我们为了保证数据的最终一致性&#xff0c;需要很多的技术方案来支持&#xff0c;比如分布式事务、分布式锁等。那具体什么是分布式锁&#xff0c;分布式锁应用在哪些业务场景、如何来实现分布式锁呢&#xff1f;今天来探讨分布式锁这个话题。 什么是…

技术交流:老刘说NLP技术公众号开通

我有一个念想&#xff1a;在当今PR文章满天飞的背景下&#xff0c;我们能够保持人间清醒&#xff0c;对NLP技术有客观、公正的了解&#xff0c;并实事求是地进行技术实践和知识共享。老刘说NLP&#xff0c;将定期发布更多、更简单、更有趣的语言知识、想法、笔记&#xff0c;包…

POJ 2453 贪心应用

文章目录1. 题目1.1 题目链接1.2 题目大意1.3 解题思路2. Accepted 代码1. 题目 1.1 题目链接 http://poj.org/problem?id2453 1.2 题目大意 一个数x的二进制表示有n个1&#xff0c;求一个有相同个数1的二进制数&#xff08;比x大&#xff0c;且要最小的&#xff09; 1.3…

ICML2020 | 一行代码就能实现的测试集上分技巧

星标/置顶小屋&#xff0c;带你解锁最萌最前沿的NLP、搜索与推荐技术文 | 苏剑林编 | 夕小瑶在训练模型的时候&#xff0c;我们需要损失函数一直训练到0吗&#xff1f;显然不用。一般来说&#xff0c;我们是用训练集来训练模型&#xff0c;但希望的是验证集的损失越小越好&…

Redis系列教程(七):Redis并发竞争key的解决方案详解

Redis高并发的问题 Redis缓存的高性能有目共睹&#xff0c;应用的场景也是非常广泛&#xff0c;但是在高并发的场景下&#xff0c;也会出现问题&#xff1a; 高并发架构系列&#xff1a;Redis缓存和MySQL数据一致性方案详解 如何解决Redis缓存雪崩、缓存穿透、缓存并发等5大难…

技术动态 | 北京大学计算机所邹磊教授研究组开源面向 RDF 知识图谱的自然语言问答系统 gAnswer...

项目网站: http://ganswer.gstore-pku.com/代码地址: https://github.com/pkumod/gAnswerOpenKG发布地址: http://openkg.cn/tool/ganswer研究组主页: http://mod.icst.pku.edu.cn一、KBQA 任务简介基于知识库的自然语言问答 (Question Answering over Knowledge Base, KBQA) 主…

POJ 2287 田忌赛马(贪心)

文章目录1. 题目1.1 题目链接1.2 题目大意1.3 解题思路2. Accepted 代码1. 题目 1.1 题目链接 http://poj.org/problem?id2287 1.2 题目大意 双方各有n匹战斗力各异的马&#xff0c;分别派出来PK&#xff0c;假设对方先出牌&#xff0c;我方后出&#xff0c;求我方最多能胜…

Netty的实现原理、特点与优势、以及适用场景

高并发编程系列 高并发编程系列&#xff1a;NIO、BIO、AIO的区别&#xff0c;及NIO的应用和框架选型 高并发编程系列&#xff1a;ConcurrentHashMap的实现原理(JDK1.7和JDK1.8) 高并发编程系列&#xff1a;CountDownLatch、Semaphore等4大并发工具类详解 高并发编程系列&…

拒绝无脑吹!从ACL20看预训练缺陷

星标/置顶小屋&#xff0c;带你解锁最萌最前沿的NLP、搜索与推荐技术文 | 舒意恒、兔子酱以 BERT 为代表作的预训练模型的研究热度一直很高&#xff0c;到 0202 年了&#xff0c;预训练的研究依旧层出不穷&#xff0c;而且 ACL 2020 Best Paper 荣誉提名也选择了这一主题的研究…

论文浅尝 | AMUSE: 基于 RDF 数据的多语言问答语义解析方法

来源&#xff1a;ISWC 2017链接&#xff1a;https://link.springer.com/content/pdf/10.1007%2F978-3-319-68288-4.pdf本文主要关注基于RDF数据的多语言问答任务中&#xff0c;对不同语言问句的语义分析工作。作者提出一种基于DUDES(Dependency-based Underspecified Discourse…

贪心应用--汽车加油次数问题

文章目录1. 问题描述2. 解题思路3. 实现代码4. 测试结果1. 问题描述 已知汽车的油箱额定里程&#xff0c;到目的地的路途中各加油站距起点的距离&#xff0c;求如何加油&#xff0c;让加油的次数最少。 2. 解题思路 每次出发前检查下一个加油站有多远&#xff0c;车子能不能…

最全多线程经典面试题和答案

Java实现线程有哪几种方式&#xff1f; 1、继承Thread类实现多线程2、实现Runnable接口方式实现多线程3、使用ExecutorService、Callable、Future实现有返回结果的多线程 多线程同步有哪几种方法&#xff1f; Synchronized关键字&#xff0c;Lock锁实现&#xff0c;分布式锁等…

论文浅尝 | Aligning Knowledge Base and Document Embedding Models

本文是我们于苏黎世大学合作的关注与知识图谱和文本对齐的论文&#xff0c;发表于ISWC2018.文本和知识图谱都包含了丰富的信息, 其中知识图谱用结构化的三元组表示信息&#xff0c;文本用自由文本形式表示信息&#xff0c;信息表示的差异给知识图谱和文本融合对齐造成了困难&am…

学会提问的BERT:端到端地从篇章中构建问答对

星标/置顶小屋&#xff0c;带你解锁最萌最前沿的NLP、搜索与推荐技术文 | 苏剑林机器阅读理解任务&#xff0c;相比不少读者都有所了解了&#xff0c;简单来说就是从给定篇章中寻找给定问题的答案&#xff0c;即“篇章 问题 → 答案”这样的流程&#xff0c;笔者之前也写过一些…

玩转算法面试-第四章查找值之leetcod相关笔记

查找问题 4-1,2 两类查找问题 1 查找有无&#xff1a;set 2 查找对应关系&#xff1a;map 常见的四种操作&#xff1a; insert&#xff0c; find&#xff0c; erase&#xff0c; change&#xff08;map&#xff09; 例题 leetcode 349 &#xff1a;给定两个数组&#xff0c;…

分治算法(Divide Conquer)

文章目录1. 分治算法思想2. 应用举例2.1 逆序度3. 分治思想处理海量数据1. 分治算法思想 分治算法的核心思想就是&#xff0c;分而治之&#xff0c;将原问题划分成n个规模较小&#xff0c;并且结构与原问题相似的子问题&#xff0c;递归地解决这些子问题&#xff0c;然后再合并…

史上最全Java多线程面试60题,含答案大赠送!

【BAT必考系列&#xff01;多线程60题】 多线程有什么用&#xff1f; 线程和进程的区别是什么&#xff1f; ava实现线程有哪几种方式&#xff1f; 启动线程方法start&#xff08;&#xff09;和run&#xff08;&#xff09;有什么区别&#xff1f; 怎么终止一个线程&#…