你见过什么什么热搜么,请问怎么实现
几个热搜榜的实现都差不多。输出都是要显示一段时间内、一定条件下的前几名搜索词。输入应该是一段时间内、一定条件下的所有搜索词
主要工作就是从所有搜索词中输出前几名的搜索词
面试时要说的工作:
第一步:计数-依次读入每个搜索词,统计出现次数;
第二步:排序-对于(搜索词,计数) 二元组,根据计数来排序
第三步:返回排序结果前几名
这个是最初的想法,可以解决部分问题,是否符合咱们的实际情况,可以进一步分析
。
如果面试官现场要代码:
Linux:cat log.txt|sort |uniq -c | sort - nr | head -n 10
进一步分析:
假设,有n个搜索词,有m次新搜索要计数
第一步:依次读入每个搜索词,统计出现次数
空间复杂性:n+m
时间复杂性:(m*log(m)+n*log(n))
散列:
时间复杂性:m*logm > m
散列的概念(hash)
经典概念:散列函数
更常见的概念:一下2者的统一
1、一一比较 -> O(1)时间的索引//顺序访问-> 随机访问
2、通过"散列函数",过大的索引存储空间->可存储的索引存储空间
散列的核心价值
查找、访问的时间:O(n) ->O(1)
散列函数
过大的索引存储空间 -> 可存储的索引存储空间
Y = 散列函数(X)
好的散列函数
人的信息 --> 名字 重名不多
人的信息 ---> 你我他 重名太多
如果以人 ---> 名字为散列函数,因为没有重名,所以秒查
如果以人 ---> 你我他为散列函数,YY,Frank,68同学都成了"他"
重名(碰撞)怎么办?–数组 链表 hash
Java里HashMap的核心部分的实现的伪码
class 散列表
数据:
散列表数组[{关键词,数据}]//散列值就是散列表数组的下标
函数:
散列值 散列函数(关键词)//假设已经有完美的散列函数,空间够用,没有碰撞:
数据 查找(关键词){
散列值=散列函数(关键词)
return 散列表数组[散列值].数据
}
更改(关键词,新数据){
散列值=散列函数(关键词)
散列表数组[散列值].数据=新数据
}
回到热搜的例子
伪代码
//热搜散列表<搜索词,技术>
例子第一步(热搜散列表,所有的新搜索词)
For(每个新搜索词)
if(热搜散列表.查找(新搜索词)==null)//get
热搜散列表.更新(新搜索词,1)
else
热搜散列表.更新新搜索词,热搜散列表.查找(新搜索词)+1)
代码
static void 例子第一步 (Map<String,Integer> 热搜,String 搜索词日志){
while(新搜索词=搜索词日志.readLine() != null){
if(热搜.get(新搜索词)==null)
热搜.put(新搜索词,1);
else
热搜.put(新搜索词,热搜.get(新搜索词)+1);
}
}
}
时间复杂性 m*lgm - m
散列是空间换时间
思路:大事化小,更具体的方法:复杂问题化简
我们学过找最大值(或最小值),好像复杂性低一些。。。
找最大值的(时间)复杂性是多少呢? n
那如果找最大的2个数呢?
1、先找最大的:时间复杂性 n
2、再找第2大的,时间复杂性还是n
因为热搜只是前k名,所以时间复杂性是k*n.
复杂性m+n的算法
鹅肠面试题
42亿条qq,给你4G内存,在O(1)复杂度找出某条qq信息,请给伪代码和代码?
解决1、把散列表放磁盘里
n*n/100条 (n=4亿条左右),有足够的硬盘,但是为了保证效率,必须在内存中使用散列表。
n字节的内存
把第一次散列的散列表读入内存
做第一次散列:所有qq消息散列大小为n/100的空间,必然会发生好多碰撞,比如说某消息第一次散列值为i;把第二次散列用的,索引为i的散列表读入内存,做第二次散列。
什么是二次散列
快速排序和合并排序的时间的复杂性是nlogn
冒泡排序的时间的复杂性是n^2
n*n/100条(n=4亿左右),有足够的硬盘,但是为了保证效率,必须在内存中使用散列表。
n字节的内存
做第一次散列:所有与qq消息散列到大小为n/100的空间,必然有好多碰撞,比如说某消息第一次散列值为i
把第二次散列用的,索引为i的散列表读入内存,做第二次散列
QQ消息--> 存在/不存在
//已知
//第一次散列:有一张散列表:
关键词(key):原QQ消息
数据(value):1-4百万之间的一个值,这个值被用作第2次散列时,标识从硬盘读哪张散列表。
//第二次散列:其实有大约4亿/100=4百万张散列表,存在硬盘里。根据第一次散列的结果取出相应的表,来进行第2次散列。
关键词(key):原QQ消息
数据(value):原QQ消息存在/不存在。
//把第一次散列的散列表读取内存
i=第一次散列表.散列函数(关键词)
//把第二次散列用的,索引为i的散列表“第二次散列表i”读入内存
散列值2=第二次散列表i.散列函数(散列值1)