解决Topk问题思路一般有两种:
找100亿个元素里面的前1000大个元素
1.将所有的元素放进数组里,调整为大堆,依次遍历1000次输出堆顶元素。
2.创建一个size为1000的数组,将1000个元素放进该数组中,并调整为小堆,堆顶元素可视为守门员,依次遍历剩余元素和当前堆顶元素进行比较,若大于堆顶元素则将堆顶元素删除,将当前元素尾插入堆,再向上调整为小堆,循环比较。
当所给元素过多时,一般使用第二种思路。一般1亿个元素占内存的100mb。
力扣373
题目描述:给定两个以升序排列的整形数组 nums1 和 nums2, 以及一个整数 k。
定义一对值 (u,v),其中第一个元素来自 nums1,第二个元素来自 nums2。
找到和最小的 k 对数字 (u1,v1), (u2,v2) … (uk,vk)。
思路:创建一个pair将数值对保存,并继承一个comparaTo接口用来比较排序。采用上述的第一种思路将所有元素放进队列里,调整为一个小堆(基于comparaTo),再循环K次取出队首元素,则为所求。记得考虑合法性校验。
import java.util.ArrayList;
import java.util.List;
import java.util.PriorityQueue;
class Pair implements Comparable<Pair>{
public int n1;
public int n2;
public int sum=n1+n2;
public Pair(int n1, int n2) {
this.n1 = n1;
this.n2 = n2;
this.sum = n1+n2;
}
@Override
//用comparaTo接口实现对pair里数对的排序,也就是实现小堆操作
public int compareTo(Pair o){
//如果this比other大,就返回>0
// 如果this比other小,就返回<0
//如果this比other大,就返回=0
//此处直接用sum值来衡量pair的大小
return this.sum-o.sum;
}
}
public class TopK {
//返回值形如二维数组,一行就是一对数,总共有k行
// [
// [num1,num2]
// [num1,num2]
// ]
public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
List<List<Integer>> result=new ArrayList<>();
//先对不合法情况进行校验
if (nums1==null||nums2==null||k<=0){
return result;
}
//采用第一种思路,将所有数对放置队列中,将队列调整为一个小堆,再循环取出k个队首元素
//调用标准库中的优先级队列
PriorityQueue<Pair> queue=new PriorityQueue<>();
for (int i=0;i<nums1.length;i++){
for (int j=0;j<nums2.length;j++){
queue.offer(new Pair(nums1[i],nums2[j]));
}
}
//循环结束后,此时所有的数对都在队列中,此时循环k次取出队首元素,加入result即为所求
//注意:这里要考虑若queue的size小于k值,则输出所有元素,对应示例3,记得考虑
for (int i=0;i<k&&!queue.isEmpty();i++){//注意是i<k,不是i<=k
Pair cur=queue.poll();//poll出来的值是数对
List<Integer> temp=new ArrayList<>();
temp.add(cur.n1);//把这些数值对先加到一维数组里面
temp.add(cur.n2);
result.add(temp);//再将一维数组加到二维数组里面
}
return result;
}
}