搜索引擎会通过日志文件把用户每次检索使用的所有检索串都记录下来,每个查询串的长度为1-255字节。

假设目前有一千万个记录(这些查询串的重复度比较高,虽然总数是1千万,但如果除去重复后,不超过3百万个。一个查询串的重复度越高,说明查询它的用户越多,也就是越热门。),请你统计最热门的10个查询串,要求使用的内存不能超过1G。

1)典型的Top K算法:还是在这篇文章里头有所阐述,详情请参见:十一、从头到尾彻底解析Hash表算法。

文中,给出的最终算法是:
第一步、先对这批海量数据预处理,在O(N)的时间内用Hash表完成统计之前写成了排序,特此订正。July、2011.04.27);
第二步、借助堆这个数据结构,找出Top K,时间复杂度为N‘logK。
即,借助堆结构,我们可以在log量级的时间内查找和调整/移动。因此,维护一个K(该题目中是10)大小的小根堆,然后遍历300万的Query,分别和根元素进行对比所以,我们最终的时间复杂度是:O(N) + N'*O(logK),(N为1000万,N’为300万)。ok,更多,详情,请参考原文。

或者:采用trie树,关键字域存该查询串出现的次数,没有出现为0。最后用10个元素的最小推来对出现频率进行排序。

2)Java实现:

1.还是制造数据,制造一亿个字符串,为了简单使用固定长度为4的字符串。(这样做的目的是为了减小hash表容量,防止溢出,因为自己制造是随机产生的字符串,不像真正的搜索引擎那样有大量的热词,所以只能这样模拟了,如果是真正的数据,就应该如上述算法所描述的那样一亿对三百万):

package com.carlosfu.chap2;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Random;


public class ProduceKeyWords {
private static int nums = 10000;
private static int wordLength = 4;
// private static int maxLength = 6;
public static void main(String[] args) throws Exception {
File file = new File("e:\\dataset\\keywords.txt");
BufferedWriter bw = new BufferedWriter(new FileWriter(file));
// Random random = new Random();
for(int i=1;i<=nums;i++){
StringBuffer buffer = new StringBuffer();
for(int j=1;j<=nums;j++){
// String temp = produce(random.nextInt(maxLength)+1); // 1 -- 6
String temp = produce(wordLength);
buffer.append(temp);
//和chap1中差不多
if(j!=nums){
buffer.append("\r");
}
}
bw.write(buffer.toString());
if(i!=nums){
bw.newLine();
}
if(i0 == 0){
System.out.println(i);
}
}
bw.flush();
bw.close();
}

private static String produce(int n){
StringBuffer word = new StringBuffer();
Random random = new Random();
for(int i=1;i<=n;i++){
char code = (char) (random.nextInt(26) + 97);
word.append(code);
}
return word.toString();
}
}

2. 一个辅助类:

package com.carlosfu.chap2;

public class KeyValue {
private String key;
private int value;
public KeyValue() {

}
public String getKey() {
return key;
}
public void setKey(String key) {
this.key = key;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
@Override
public String toString() {
return "KeyValue [key=" + key + ", value=" + value + "]";
}

}
3. TOP K算法的实现,实际上就是一个堆排序,感觉hashtable的values()和keys()真是不太好用,可能是我不太会用,所以在构造完hashtable后,我又用KeyValue[]来代替他,这样确实比较费内存,以后再改吧,暂时忘记咋改了:

package com.carlosfu.chap2;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.Hashtable;
import java.util.Iterator;

//结果:
//456976
//KeyValue [key=xcpi, value=296]
//KeyValue [key=tqpf, value=292]
//KeyValue [key=oufk, value=291]
//KeyValue [key=ctqy, value=287]
//KeyValue [key=ngky, value=283]
//KeyValue [key=hdap, value=283]
//KeyValue [key=ljpi, value=282]
//KeyValue [key=lgkb, value=282]
//KeyValue [key=ahis, value=282]
//KeyValue [key=upcm, value=282]
//KeyValue [key=yzyy, value=282]
//KeyValue [key=klow, value=281]
public class TopKWords {
private static int topK = 10;
public static void main(String[] args) throws Exception {
String path = "e:\\dataset\\keywords.txt";
BufferedReader br = new BufferedReader(new FileReader(new File(path)));
Hashtable<String, Integer> table = new Hashtable<String, Integer>();
int j = 0;
for(String str = br.readLine();str!=null;){

Integer num = table.get(str);
if(num == null){
table.put(str, 1);
}else{
table.put(str, num + 1);
}
j++;
System.out.println("record: " + j);
System.out.println("table: " + table.size());
str = br.readLine();
}
System.out.println(table.keySet().size());

//上面的无需多做解释


Iterator<String> it1 = (Iterator<String>) table.keys();
Iterator<Integer> it2 = table.values().iterator();


int length = table.values().size();

//构造KeyValue[]

KeyValue[] a = new KeyValue[length+1];
for(int i=1;i<a.length;i++){
KeyValue node = new KeyValue();
if(it1.hasNext() && it2.hasNext()){
node.setKey(it1.next());
node.setValue(it2.next());
}
a[i] = node;

}
//堆排序算法

// int[] a = new int[length+1];

int n = length;
createHeap(a,n);
System.out.println(a[1]);
//输出每个堆的头
for(int i=n;i>=n-topK ;i--){
KeyValue temp = a[i];
a[i] = a[1];
a[1] = temp;
// produceMax(a,1,i-1);
createHeap(a,i-1);
System.out.println(a[1]);
}



}
private static void createHeap(KeyValue[] a,int n){
int length = n/2;
for(int i=length;i>=1;i--){
produceMax(a,i,n);
}
}
private static void produceMax(KeyValue[] a, int i, int n) {
int left = 2 * i;
int right = 2 * i + 1;
int max = i;
if(left <= n && a[left].getValue()>a[max].getValue() ){
max = left;
}
if(right <= n && a[right].getValue()>a[max].getValue()){
max = right;
}
if(max!=i){
KeyValue temp = a[i];
a[i] = a[max];
a[max] = temp;
produceMax(a,max,n); //把max以后的也要符合堆的规则
}
}
}
4. 附上一个简单的堆排序算法和一个堆排序的演示动画:

package com.carlosfu.chap2;
public class HeapSort2 {
public static void main(String[] args) {
int[] a = new int[9];
a[1] = 46;
a[2] = 55;
a[3] = 13;
a[4] = 42;
a[5] = 94;
a[6] = 5;
a[7] = 17;
a[8] = 70;
print(a);
int n = a.length-1;
createHeap(a,n);
for(int i=n;i>=2;i--){
int temp = a[i];
a[i] = a[1];
a[1] = temp;
// produceMax(a,1,i-1);
createHeap(a,i-1);
}
print(a);
}


private static void createHeap(int[] a,int n){
int length = n/2;
for(int i=length;i>=1;i--){
produceMax(a,i,n);
}
}
private static void produceMax(int[] a, int i, int n) {
int left = 2 * i;
int right = 2 * i + 1;
int max = i;
if(left <= n && a[left]>a[max] ){
max = left;
}
if(right <= n && a[right]>a[max]){
max = right;
}
if(max!=i){
int temp = a[i];
a[i] = a[max];
a[max] = temp;
produceMax(a,max,n);
}
}

private static void print(int[] a) {
int n = a.length - 1;
for (int i = 1; i <= n; i++) {
System.out.print(a[i] + " ");
}
System.out.println();
}
}
原理动画:在flash中输入46,55,13,42,94,5,17,70

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值