Java Web场景篇之TopK/秒杀场景等

KMP算法(一种改进的字符串匹配算法)

参考:
最通俗易懂的KMP算法详解
详解KMP算法

TopK问题

有一个1G大小的一个文件,里面每一行是一个词,词的大小不超过16字节,内存限制大小是1M。返回频数最高的100个词。

  • ①分治:顺序读文件,对每个词x取Hash(x)%2000,按照该值存到2000个小文件中。每个文件是500k左右。如果有文件超过了1M则继续分割。O(N)
  • ②Trie树/Hash_map:字符串用Trie树最好。对每个小文件,统计其中出现的词频。O(N)*(平均字符长度),长度一般是常数,也就是O(N).
  • ③小顶堆:用容量为100的小顶堆,以频率为value值插入,取每个文件现频率最大的100个词,把这100个词及相应的频率存入文件。最差O(N)lg(100),也就是O(N).注:2,3步骤合起来需要一轮磁盘存取过程。存入文件的个数可以缩减一下,因为主要开销在磁盘读取上,减少文件读取次数,可以在每个文件存取最大容量的字符数量,比如这道题1(M/16字节字符串长度+频率(int)8字节)的数存到一个文件中。比如20000个词存在一个文件中,可以缩减到10个文件。这样最后一步只需要读取10次就可以了。
  • ④归并:将得到的10个文件里面的数进行归并,取前100个词。注:我觉得其实不需要多路归并,因为只需要找top100的数,归并排序首先是nlgn的复杂度,第二是频繁的磁盘存取,这里最好是还是在内存建立容量为100的小顶堆,依次读文件,遍历每个文件中的元素更新小顶堆,这样只需10次存取,并且时间复杂度是nlog100,也就是O(n)的。

注释:为什么说用Trie树好,我之前一直没想明白,因为网上说Trie树是空间换时间,而这道题是空间敏感呀的。

总结了一下,其实是两点我没想明白:

  • 1.字符串会通过一个hash算法(BKDRHash,APHash,DJBHash,JSHash,RSHash,SDBMHash,可以自己看一下,基本就是按位来进行hash的)映射为一个正整数然后对应到hash表中的一个位置,表中记录的value值是次数,这样统计次数只需要将字符串hash一下找到对应位置把次数+1就行了。如果这样的话hash中是不是不用存储字符串本身?如果不存储字符串本身,那应该是比较省空间的。而且效率的话因为Tire树找到一个字符串也是要按位置比较一遍,所以效率差不多呀。但是,其实字符串的hash是要存储字符串本身的,不管是开放地址法还是散列表法,都无法做到不冲突。除非桶个数是字符串的所有情况26^16,那是肯定空间不够的,因此hash表中必须存着字符串的值,也就是key值。字符串本身,那么hash在空间上肯定是定比不过Trie树的,因为Trie树对公共前缀只存储一次。
  • 2.为什么说Trie树是空间换时间呢,我觉得网上这么说不甚合理,这句话其实是相对于二叉查找树来说的,之所以效率高,是因为二叉查找树每次查找都要比较大小,并且因为度为2,查找深度很大,比较次数也多,因此效率差。而Trie树是按位进行hash的,比如26个字母组成的字符串,每次找对应位的字符-‘a’就是位置了。而且度是26,查找深度就是字符串位数,查找起来效率自然就很快。但是为啥说是空间换时间,是因为字符串的Trie树若想存储所有的可能字符串,比如16位,一个点要对应下一位26种情况,也就是26个分支,也得26^16个位置,所以空间是很大的。但是Trie树的话可以采用依次插入的,不需要每个点记录26个点,而是只存在有值的分支,Trie树节点只要存频率次数,插入的流程就是挨个位子找分支,没有就新建,有就次数+1就行了。因此空间上很省,因为重复前缀就统计一次,而效率很高,O(length)。

参考:
海量数据处理题目分析(推荐)
面试经典的海量数据处理(TOPK)问题—转载+个人见解
海量数据中找出TopK
字典树(Trie树)实现与应用

业务-秒杀场景如何设计

参考:
如何设计一个秒杀系统
如何设计一个秒杀程序及避免超卖问题

你可能感兴趣的:(Java Web场景篇之TopK/秒杀场景等)