算法:(九)堆

回来了!实习了两个月,没有更新博客,现在正常更新,会将实习用到的技术栈更新一下…

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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值