题目描述
淘宝 web 服务器上有 1 个 access 日志文件,记录着用户访问的 url,url 总数100 亿以上,每个 url 约占 64 字节,这些 url 可能存在重复,在一个内存只有 2G 的机器上,统计出访问频率最高的前100 个 URL。
考察点
- MapReduce 思想,利用中间文件存储,分而治之。
- 排序算法
解决方案
内存 2G = 2 ^ 11 byte, 64 byte = 2^6 byte 2^11/2^6 = 2 ^ 5 = 32 分成约100个文件
- 对url做hash运算,然后对将结果对100求余,放入以此余数为结尾的文件中(由于hash特性,所有相同url都会被放入到同一个文件中)
- 分别对100个文件中的url进行统计,统计各url的出现次数
- 对100个文件url的统计次数取前100个(使用堆排序),如此之后,每个文件最多会有100条记录
- 100个文件 * 每个文件的记录数100 = 10000,对些10000个文件进行排序,取出前100个,即为访问频率最高的100个URL(可以借鉴小顶堆思想,构建100个元素的堆,始终保持堆中是访问频率最高的前100个URL)
package alibaba;
import java.util.Arrays;
import java.util.Random;
public class SmallTopHeapSortTest {
public static class SmallHeapBox {
int sz;
int half;
int[] arr;
public SmallHeapBox(int n) {
this.sz = n + 1;
this.half = this.sz / 2;
this.arr = new int[this.sz];
}
public boolean insert(int value) {
int top = 1;
if (arr[top] > value) {
return false;
}
arr[top] = arr[arr.length - 1];
arr[arr.length - 1] = value;
for (int i = half; i >= 1; i--) {
int right = 2 * i + 1;
if (right < this.sz && this.arr[right] < this.arr[i]) {
swap(this.arr, i, right);
}
int left = 2 * i;
if (left < this.sz && this.arr[left] < this.arr[i]) {
swap(this.arr, i, left);
}
}
return true;
}
public int[] obtainResult() {
return this.arr;
}
private void swap(int[] arr, int i, int j) {
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
//测试代码
public static void main(String[] args) {
int N = 1000;
int[] arr = new int[N];
Random random = new Random(System.currentTimeMillis());
for (int i = 0; i < arr.length; i++) {
arr[i] = random.nextInt(200);
}
SmallHeapBox smallHeapBox = new SmallHeapBox(N);
for (int i = 0; i < arr.length; i++) {
smallHeapBox.insert(arr[i]);
}
Arrays.sort(arr);
int[] result = smallHeapBox.obtainResult();
Arrays.sort(result);
for (int i = 0; i < N; i++) {
if (arr[i] != result[i + 1]) {
System.out.println("不一致");
}
System.out.printf("", arr[i]);
}
}
}