回来了!实习了两个月,没有更新博客,现在正常更新,会将实习用到的技术栈更新一下…
9.1 堆的应用
面试题59:数据流的第k大数字
题目:请设计一个类型KthLargest,它每次从一个数据流中读取一个数字,并得出数据流已经读取的数字中第k(k>=1)大的数字。该类型的构造函数有两个参数,一个是整数k,另一个是包含数据流中最开始数字的整数数组nums(假设数组nums的长度大于k)。该类型还有一个函数add,用来添加数据流中的新数字,并返回数据流中已经读取的数字的第k大数字。
public class KthLargestInStream {
/**
* PriorityQueue默认是一个小顶堆
*/
private PriorityQueue<Integer> heap;
int size;
public KthLargestInStream_59(int k, int[] nums) {
heap = new PriorityQueue<>();
size = k;
for(int num : nums){
heap.add(num);
}
}
/**
* 构造一个容量为k的小顶堆,每次返回的都是堆顶元素
* 每次添加元素时,若堆中元素小于k,则直接添加,若堆中元素等于k,则比较添加的元素与堆顶的元素的大小,选择添加还是抛弃即可
* @param num
* @return
*/
public int add(int num){
if(size > heap.size()){
heap.offer(num);
} else {
if(num > heap.peek()){
heap.poll();
heap.offer(num);
}
}
return heap.peek();
}
}
面试题60:出现频率最高的k个数字
题目:请找出数组中出现频率最高的k个数字。例如,当k等于2时,[1,2,2,1,3,1],由于数字1出现了3次,数字2出现了2次,数字3出现了1次,因此出现频率最高的2个数字是1和2。
/**
* HashMap统计出现次数 + 数量为k的小顶堆
* @param nums
* @param k
* @return
*/
public int[] topkFrequent(int[] nums, int k){
HashMap<Integer, Integer> map = new HashMap();
for(int num : nums){
map.put(num, map.getOrDefault(num, 0) + 1);
}
// 构造出现频次的小顶堆
PriorityQueue<Map.Entry<Integer, Integer>> heap = new PriorityQueue<>((x, y) -> x.getValue() - y.getValue());
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
if(k > heap.size()){
heap.offer(entry);
} else {
if(heap.peek().getValue() < entry.getValue()){
heap.poll();
heap.offer(entry);
}
}
}
return heap.stream().mapToInt(i -> i.getKey()).toArray();
}
面试题61:和最小的k个数对
题目:给定两个递增排序的整数数组,从两个数组中各取一个数字u和v组成一个数对(u,v),请找出和最小的k个数对。例如,输入两个数组[1,5,13,21],[2,4,9,15],和最小的3个数对为(1, 2)、(1, 4)、(5, 2)。
/**
* 大顶堆,时间复杂度O(k^2logk)
* 只判断每个数组的前k个数,即可保证能够找到前k个最小的组合
* @param nums1
* @param nums2
* @param k
* @return
*/
public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
PriorityQueue<List<Integer>> heap = new PriorityQueue<>((x, y) -> y.get(0) + y.get(1) - x.get(0) - x.get(1));
for (int i = 0; i < Math.min(k, nums1.length); i++) {
for (int j = 0; j < Math.min(k, nums2.length); j++) {
if (heap.size() < k) {
List<Integer> list = new ArrayList<>();
list.add(nums1[i]);
list.add(nums2[j]);
heap.offer(list);
} else if (heap.peek().get(0) + heap.peek().get(1) > nums1[i] + nums2[j]) {
heap.poll();
List<Integer> list = new ArrayList<>();
list.add(nums1[i]);
list.add(nums2[j]);
heap.offer(list);
}
}
}
return new ArrayList<>(heap);
}
/**
* 小顶堆,时间复杂度更低O(klogk)
* 构造一个小顶堆判断当前最小的组合,先将nums1所有的下标和nums2下标0组合放到小顶堆中
* 然后将最小的值弹出放到结果集合result中
* 对当前弹出的结果,放入nums2下一个位置的下标组合,可以保证nums1和nums2所有的位置都被访问到
* @param nums1
* @param nums2
* @param k
* @return
*/
public List<List<Integer>> kSmallestPairsPro(int[] nums1, int[] nums2, int k) {
PriorityQueue<List<Integer>> heap = new PriorityQueue<>(
(x, y) -> nums1[x.get(0)] + nums2[x.get(1)] - nums1[y.get(0)] - nums2[y.get(1)]);
if (nums2.length > 0) {
for (int i = 0; i < Math.min(k, nums1.length); i++) {
heap.offer(Arrays.asList(i, 0));
}
}
List<List<Integer>> result = new ArrayList<>();
while (k-- > 0 && !heap.isEmpty()) {
List<Integer> ids = heap.poll();
result.add(Arrays.asList(nums1[ids.get(0)], nums2[ids.get(1)]));
if (ids.get(1) < nums2.length - 1) {
heap.offer(Arrays.asList(ids.get(0), ids.get(1) + 1));
}
}
return result;
}