博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
面试题:寻找热门查询
阅读量:6234 次
发布时间:2019-06-21

本文共 5619 字,大约阅读时间需要 18 分钟。

搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的长度为1-255字节。假设目前一个日志文件中有一千万个记录(这些查询串的重复度比较高,虽然总数是1千万,但如果除去重复后,不超过3百万个。一个查询串的重复度越高,说明查询它的用户越多,也就是越热门),请你统计最热门的10个查询串,要求使用的内存不能超过1G。

1000万条记录,每条记录最大为255Byte,那么日志文件最大有2.5G左右,大于1G内存。但是题目中又提到这样的1000万条记录中有许多是重复的,出去重复的话只有300万条记录,存储这样的300万条记录需要0.75G左右的内存,小于1G内存。那么我们可以考虑将这些无重复的记录装入内存,这是我们需要一种数据结构,这种数据结构即能够存储查询串,又能存储查询串的出现次数,我们可以通过hashmap<query,count>来保存。读取文件,创建一个hashmap,如果hashmap中存储了遍历到的query,则修改该query所对应的count值,使其+1;如果hashmap中没有这个query,那么往haspmap中插入<query,1>。这样我们就创建好了一个包含所有query和次数的hashmap。

然后我们创建一个长度为10最大堆MaxHeap这里应该是最小堆MinHeap,求最多的要用最小堆,求最小的要用最大堆,ps:2012-10-8),最小堆的堆顶元素最小,如果堆顶这个最小的元素都大于其他非堆元素了,那么堆中的其他元素必定大于其他非堆中元素。遍历hashmap,如果MaxHeap未满,那么往MaxHeapMinHeap中插入这个键值对,如果MinHeap满了,则比较遍历到的元素的count值堆顶的count,如果遍历到元素的count大于堆顶count值,删除堆顶元素,插入当前遍历到的元素。遍历完整个hashmap以后,在MaxHeapMinHeap中存储的就是最热门10个查询串。

代码实现:

花了一天时间才写出这道题目的具体代码实现。具体思路前面已经说过了,主要分为以下几步:

首先我们遍历words.txt这个文件,并且创建一个hashmap,其中key就是words.txt中的查询串,而value则是这个查询穿出现的次数。这里通过判断key是否存在,如果不存在则put(key,1);如果存在的话,则先求value=get(key),然后put(key,value+1),这里的put相当于是修改value的值。

在构建好hashmap以后,我们需要创建一个最小堆(我们这里有LinkedList实现),假如堆没有满,我们将hashmpa中的元素放入到最小堆中,如果满的话,则比较hashmap中元素的value值与堆顶元素的value值,如果大于堆顶元素的value值,则删除堆顶元素,然后将这个hashmap元素插入到堆中,在调整堆结构,使其满足最小堆结构。

在遍历完hashmap以后,我们的最小堆中保存的元素就是最热门查询。

View Code
import java.io.BufferedReader;import java.io.FileNotFoundException;import java.io.FileReader;import java.util.HashMap;import java.util.Iterator;import java.util.LinkedList;import java.util.Map;import java.util.Map.Entry;public class GetPopularQuery {    public static void main(String args[]) {        GetPopularQuery gpq = new GetPopularQuery();        Map
content = new HashMap
(); gpq.buildHashMap(content); // gpq.printHashMap(content); int k = 10; gpq.findPopularQuery(content, k); } // 第一步:创建hashmap public void buildHashMap(Map
content) { try { FileReader reader = new FileReader("words.txt"); BufferedReader br = new BufferedReader(reader); String s = null; while ((s = br.readLine()) != null) { int count; if (!content.containsKey(s))// 如果不存在这个key那么就插入这个key,其值为1 { content.put(s, 1); } else// 修改hashmpa中的值,直接使用put覆盖,不需要remove后再put { count = content.get(s); content.put(s, count + 1); } } br.close(); reader.close(); } catch (Exception e) { e.printStackTrace(); } } // 打印hashmap中的值 public void printHashMap(Map
content) { Iterator it = content.entrySet().iterator(); while (it.hasNext()) { Entry entry = (Entry) it.next(); // entry.getKey() 返回与此项对应的键 // entry.getValue() 返回与此项对应的值 System.out.println(entry.getKey() + " " + entry.getValue()); } } // 查找最热门查询 public void findPopularQuery(Map
content, int k) { LinkedList
> list = new LinkedList
>();// 使用ListedList来创建最大堆 int count = 0; Iterator it = content.entrySet().iterator(); while (it.hasNext() && count < k) { // 首先将hashmap中前10个元素放入ListedList当中。 Entry entry = (Entry) it.next(); // entry.getKey(); //返回与此项对应的键 // entry.getValue();// 返回与此项对应的值 // System.out.println(entry.getKey()+" "+entry.getValue()); list.add(entry); count++; } //输出list中元素 for (int i = 0; i < count; i++) { System.out.println(list.get(i).getKey() + " " + list.get(i).getValue()); } System.out.println("----------------------"); buildHeap(list, k);//构建最大堆,里面保存有LinkedList中的前k个元素。 int len = content.size() - 1;//hashmap中总共的元素个数。 while(it.hasNext()) { Entry
entry = (Entry
) it.next(); if(entry.getValue()>list.get(0).getValue()) { list.set(0, entry); adjustHeap(list, 0, k); } } //输出最热门查询 for (int i = 0; i < count; i++) { System.out.println(list.get(i).getKey() + " " + list.get(i).getValue()); } } //构建最小堆 public void buildHeap(LinkedList
> list, int k) { int nonleaf = k / 2 - 1;// for (int i = nonleaf; i >= 0; i--) { adjustHeap(list, i, k); } } //调整最小堆 public void adjustHeap(LinkedList
> list, int parent,int k) { int left = parent * 2 + 1;// 左节点 while (left < k) { if (left + 1 < k && list.get(left).getValue() > list.get(left + 1) .getValue()) left++;// 此时left代表右节点 if (list.get(parent).getValue() <= list.get(left).getValue())//最小堆 break; else { swap(list, parent, left); parent = left; left = parent * 2 + 1; } } } // 交换LinkedList中元素值 public void swap(LinkedList
> list, int i, int j) { Entry temp = list.get(i); list.set(i, list.get(j)); list.set(j, temp); }}

 

百度面试题:将query按照出现的频度排序(10个1G大小的文件)。有10个文件,每个文件1G,每个文件的每一行都存放的是用户的query,每个文件的query都可能重复。如何按照query的频度排序?

网上给出的答案:

1)读取10个文件,按照hash(query)%10的结果将query写到对应的10个文件(file0,file1....file9)中,这样的10个文件不同于原先的10个文件。这样我们就有了10个大小约为1G的文件。任意一个query只会出现在某个文件中。

2)对于1)中获得的10个文件,分别进行如下操作
     - 利用hash_map(query,query_count)来统计每个query出现的次数。
     - 利用堆排序算法对query按照出现次数进行排序。
     - 将排序好的query输出的文件中。
    这样我们就获得了10个文件,每个文件中都是按频率排序好的query。
3)对2)中获得的10个文件进行归并排序,并将最终结果输出到文件中。
注:如果内存比较小,在第1)步中可以增加文件数。

我的答案

1)读取10个文件,按照hash(query)%10的结果将query写到对应的10个文件(file0,file1....file9)中,这样的10个文件不同于原先的10个文件。这样我们就有了10个大小约为1G的文件。任意一个query只会出现在某个文件中。

2)对于1)中获得的10个文件,分别进行如下操作

  • 利用hash_map(query,query_count)来统计每个query出现的次数。
  • 创建一个长度为10的堆来保存一个文件中出现次数最多的hash_map(query,query_count),最后将这10个键值对输出到result文件中。

3)通过2)获得的result文件保存着每个文件出现次数最多的10条记录,对其中的100条记录按照query_count进行排序,最后输出query_count最大的10条query。

注:如果内存比较小,在第1)步中可以增加文件数。

 

转载于:https://www.cnblogs.com/xwdreamer/archive/2012/05/09/2492674.html

你可能感兴趣的文章
MHA failover GTID 专题
查看>>
如何在windows中使用cmd命令去编译,运行C++程序
查看>>
《机器人自动化:建模、仿真与控制》——导读
查看>>
BitTorrent可被用来放大拒绝服务攻击
查看>>
Web 缓存欺骗攻击技术详解
查看>>
容器网络方面的挑战和要求
查看>>
晶澳与马士基强强联合 全球业务进一步升级
查看>>
希捷固态硬盘:另类主控重出江湖
查看>>
来自编程“老者”们的须时刻谨记的七大教训金典
查看>>
漫谈深度学习 这个领域有点火!
查看>>
探究OpenShift市场:广度与深度
查看>>
如何使用SilentCleanup绕过UAC?
查看>>
开放容器计划能否改善容器安全性?
查看>>
任性AWS 大范围升级进行时
查看>>
医学影像大数据与智能医疗
查看>>
做好数据挖掘模型的9条经验总结
查看>>
集算器协助java处理结构化文本之对齐连接
查看>>
最大银行遭黑客数小时攻击 俄罗斯力推个人数据本地化
查看>>
Li-Fi很美好,但短期内未必能取代Wi-Fi
查看>>
详解Linux进程及作业管理
查看>>