八、Hash应用例子
搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的长度为1-255字节。假设目前有一千万个记录(这些查询串的重复度比较高,虽然总数是1千万,但如果除去重复后,不超过3百万个。一个查询串的重复度越高,说明查询它的用户越多,也就是越热门。),请你统计最热门的10个查询串,要求使用的内存不能超过1G。
第一步:Query统计
    1、直接排序法
首先我们最先想到的的算法就是排序了,首先对这个日志里面的所有Query都进行排序,然后再遍历排好序的Query,统计每个Query出现的次数了。 
  但是题目中有明确要求,那就是内存不能超过1G,一千万条记录,每条记录是255Byte,很显然要占据2.375G内存,这个条件就不满足要求了。
   让我们回忆一下数据结构课程上的内容,当数据量比较大而且内存无法装下的时候,我们可以采用外排序的方法来进行排序,这里我们可以采用归并排序,因为归并排序有一个比较好的时间复杂度O(nlogn)。
  排完序之后我们再对已经有序的Query文件进行遍历,统计每个Query出现的次数,再次写入文件中。 
  综合分析一下,排序的时间复杂度是O(nlogn),而遍历的时间复杂度是O(n),因此该算法的总体时间复杂度就是O(n+nlogn)=O(nlogn)。
   2、Hash Table法 
  在第1个方法中,我们采用了排序的办法来统计每个Query出现的次数,时间复杂度是O(nlogn),那么能不能有更好的方法来存储,而时间复杂度更低呢?
   题目中说明了,虽然有一千万个Query,但是由于重复度比较高,因此事实上只有300万的Query,每个Query 255Byte,因此我们可以考虑把他们都放进内存中去,而现在只是需要一个合适的数据结构,在这里,Hash Table绝对是我们优先的选择,因为Hash Table的查询速度非常的快,几乎是O(1)的时间复杂度。
   那么,我们的算法就有了:维护一个Key为Query字串,Value为该Query出现次数的HashTable,每次读取一个Query,如果该字串不在Table中,那么加入该字串,并且将Value值设为1;如果该字串在Table中,那么将该字串的计数加一即可。最终我们在O(n)的时间复杂度内完成了对该海量数据的处理。
   本方法相比算法1:在时间复杂度上提高了一个数量级,为O(n),但不仅仅是时间复杂度上的优化,该方法只需要IO数据文件一次,而算法1的IO次数较多的,因此该算法2比算法1在工程上有更好的可操作性。
第二步:找出Top 10
        算法一:普通排序 
    我想对于排序算法大家都已经不陌生了,这里不在赘述,我们要注意的是排序算法的时间复杂度是O(nlogn),在本题目中,三百万条记录,用1G内存是可以存下的。
 
  算法二:部分排序 
  题目要求是求出Top 10,因此我们没有必要对所有的Query都进行排序,我们只需要维护一个10个大小的数组,初始化放入10个Query,按照每个Query的统计次数由大到小排序,然后遍历这300万条记录,每读一条记录就和数组最后一个Query对比,如果小于这个Query,那么继续遍历,否则,将数组中最后一条数据淘汰,加入当前的Query。最后当所有的数据都遍历完毕之后,那么这个数组中的10个Query便是我们要找的Top10了。
  不难分析出,这样,算法的最坏时间复杂度是N*K, 其中K是指top多少。
 
  算法三:堆 
  在算法二中,我们已经将时间复杂度由NlogN优化到NK,不得不说这是一个比较大的改进了,可是有没有更好的办法呢?
   分析一下,在算法二中,每次比较完成之后,需要的操作复杂度都是K,因为要把元素插入到一个线性表之中,而且采用的是顺序比较。这里我们注意一下,该数组是有序的,一次我们每次查找的时候可以采用二分的方法查找,这样操作的复杂度就降到了logK,可是,随之而来的问题就是数据移动,因为移动数据次数增多了。不过,这个算法还是比算法二有了改进。
   基于以上的分析,我们想想,有没有一种既能快速查找,又能快速移动元素的数据结构呢?回答是肯定的,那就是堆。
   借助堆结构,我们可以在log量级的时间内查找和调整/移动。因此到这里,我们的算法可以改进为这样,维护一个K(该题目中是10)大小的小根堆,然后遍历300万的Query,分别和根元素进行对比。
  思想与上述算法二一致,只是算法在算法三,我们采用了最小堆这种数据结构代替数组,把查找目标元素的时间复杂度有O(K)降到了O(logK)。
   那么这样,采用堆数据结构,算法三,最终的时间复杂度就降到了N‘logK,和算法二相比,又有了比较大的改进。 
   
 总结:
   至此,算法就完全结束了,经过上述第一步、先用Hash表统计每个Query出现的次数,O(N);然后第二步、采用堆数据结构找出Top 10,N*O(logK)。所以,我们最终的时间复杂度是:O(N)+N'*O(logK)。(N为1000万,N’为300万)。如果各位有什么更好的算法,欢迎留言评论。

Hash应用例子的更多相关文章

  1. Hash破解神器:Hashcat的简单使用

    Hash破解神器:Hashcat的简单使用 2014-06-10 21:02:42|  分类: 离线密码破解 |  标签:密码字典  rar密码破解  zip密码破解  密码破解  |举报|字号 订阅 ...

  2. 一致性hash在分布式系统中的应用

    场景 如果要设计一套KV存储的系统,用户PUT一个key和value,存储到系统中,并且提供用户根据key来GET对应的value.要求随着用户规模变大,系统是可以水平扩展的,主要要解决以下几个问题. ...

  3. backbone库学习-Router

    backbone库的结构http://www.cnblogs.com/nuysoft/archive/2012/03/19/2404274.html 本文的例子来自http://blog.csdn.n ...

  4. Router

    backbone库学习-Router backbone库的结构http://www.cnblogs.com/nuysoft/archive/2012/03/19/2404274.html 本文的例子来 ...

  5. 复写equals、hashCode和toString方法

    equals.hashCode和toString 这三个方法都是object类的方法,由于所有的类都是继承这个类,所以每一个类都有这三个方法. 1.复写equals方法 原则: 首先,两个实例是相同的 ...

  6. 第四章:Python基础の快速认识內置函数和操作实战

    本課主題 內置函数介紹和操作实战 装饰器介紹和操作实战 本周作业 內置函数介紹和操作实战 返回Boolean值的內置函数 all( ): 接受一個可以被迭代的對象,如果函数裡所有為真,才會真:有一個是 ...

  7. stylus笔记(二)

    1.方法 函数  Stylus强大之处就在于其内置的语言函数定义.其定义与混入(mixins)一致:却可以返回值. 默认参数 可选参数往往有个默认的给定表达.在Stylus中,我们甚至可以超越默认参数 ...

  8. C#基础复习(4) 之 浅析List、Dictionary

    参考资料 [1] .netCore 源码 https://github.com/dotnet/corefx [2] <Unity 3D脚本编程 使用C#语言开发跨平台游戏>陈嘉栋著 [3] ...

  9. Chained Declustering

          此论文描述了在无共享架构的多处理器机器上的数据库系统的数据冗余分布方法.该方法提高了系统的可用性,同时在单节点故障的情况下,可以很好的实现负载均衡.以下是论文的一些摘要,详细可以参见论文原 ...

随机推荐

  1. echarts获取数据的一些难点1

    像上面获取数据后,如果再根据下方按钮查询不同获取的价格,虽然曲线价格能按照不同的来展示, 但是问题有: 查询到的company字段虽然在获取的data中能测试出,但是在上方填入这些companys后, ...

  2. Jenkins简介

    Jenkins 是一个开源项目,提供了一种易于使用的持续集成系统,使开发者从繁杂的集成中解脱出来,专注于更为重要的业务逻辑实现上.同时 Jenkins 能实施监控集成中存在的错误,提供详细的日志文件和 ...

  3. python中的print()、str()和repr()的区别

    print()函数,我们可以看出,在Python IDLE中直接输入的字符串都是有类型的,而print打印后的字符串相当于一串文字,把字符串的引号也省略了,没有类型 print()函数,生成可读性更好 ...

  4. SQL中的float类型的数据

    问题1.  如何在SQL中默认的使用float类型的数据 SQL中想要通过计算的方式最快的得到一个float类型的数据,只需要运算的其中一个值后面加上小数点就ok. 比如 :9/2=4 但是 :9/2 ...

  5. MySQL的正则表达式的LIKE和REGEXP区别

    LIKE匹配整个列.如果被匹配的文本在列值 中出现,LIKE将不会找到它,相应的行也不被返回(除非使用 通配符).而REGEXP在列值内进行匹配,如果被匹配的文本在 列值中出现,REGEXP将会找到它 ...

  6. 记录flask使用模板时出现的“Internal Server Error”错误

    在看<Flask Web开发实战:入门.进阶与原理解析(李辉著 )>时照着书上的代码抄了一遍,然后运行时发现一直出现以下的错误 书上的源代码如下 watchlist.html <he ...

  7. openstack环境搭建常用命令

    1,编辑/etc/selinux/config文件,关闭selinux SELINUX=disabled 2,清空iptables规则并保存 # iptables -F # service iptab ...

  8. 在Windows下解决git ERROR: Permission to XXX.git denied to user

    这种情况一般都是由于登陆了不同的git仓库在本地记录了凭证导致的,比如登陆了两个不同的github账号. 1.控制面板 2.删除凭证再重新提交将会重新输入用户名和密码 以上.

  9. 02-python-垃圾回收机制

    转载自 https://www.cnblogs.com/Xjng/p/5128269.html 加以整理,方便记忆 1垃圾回收机制 Python中的垃圾回收是以引用计数为主,分代收集为辅.引用计数的缺 ...

  10. Oracle:新增用户登录提示“ORA-04098:触发器‘GD.ON_LOGON_TRIGGER’无效且未通过重新验证”

    接着上一篇创建一个只有查看权限的用户,在测试环境,新建账号后尝试登录,提示如下: 1.看提示是base库的触发器有问题了,所以先定位到这个触发器 SELECT * FROM DBA_OBJECTS W ...