Priority Queue Summary

建堆

堆的性质, 以小根堆为例子, 父亲的节点不大于儿子的节点。 这样的数据结构成为堆

Insert

插入操作:插入在最后面,然后和自己的父亲相交换直到不大于自己的父亲节点。 复杂度 log(n)

pop

删除操作, 把最后一个元素与第一个元素交换,在向下交换。 复杂度 log(n)

建堆

因为是从底向上建的,这样,有1/2的元素向下比较了一次,有1/4的向下比较了两次,1/8的,向下比较了3次,……,1/2^k的向下比较了k次,其中1/2^k <= 1。 所以所有移动的总和为 $\sum_{k=1}^{logn} \Big[\frac{n}{2^{k}} * k\Big]$ 所以最后经过计算就是 O(n) 复杂度

import java.util.*;
import java.lang.*;
class Heap{
    int[] heap;
    int sz = 0;
    public Heap(int[] items) {
        this.heap = items;
        this.sz = items.length;
        int i = items.length - 1;
        while(i >= 0) {
            heapify(i);
            i--;
        }
        for(int j : heap) System.out.print(j + " ");
        System.out.println("HEAP builded");
    }
    public void push(int x) {
        int i = sz++;
        while(i > 0) {
            int parent = (i - 1) / 2;
            if(heap[parent] <= x) break;
            heap[i] = heap[parent];
            i = parent;
        }
        heap[i] = x;
    }
    public int pop() {
        if(sz  == 0) return -1;
        int ret = heap[0];
        this.sz--;
        heap[0] = heap[sz];
        heapify(0);
        return ret;
    }

    public void heapify(int x) {
        int i  = x;
        while(i * 2 + 1 < sz) {
            int l = i * 2 + 1, r = i * 2 + 2;
            if(r < sz && heap[r] < heap[l]) l = r;
            if(heap[l] > heap[i]) break;
            int tmp = heap[i];
            heap[i] = heap[l];
            heap[l] = tmp;
            i = l;
        }
    }

    public static void main(String[] args) {
        int[] item = new int[]{5,4,3,2,1,9,6,7,4};
        Heap test = new Heap(item);
        int ret = 0;
        while(ret != -1) {
            ret = test.pop();
            System.out.print(ret + " ");
        }
    }
}

经典案例

Argus

编写一个系统, 该系统支持一个 register 命令。来注册不同时间, 且每个 period 时间, 就会才生一个 Q_num 事件。 输出前 k 个时间。
这个解法就是直接用一个 priorityqueue 每次取出当前事件,之后在 push 一个新的时间到 pq 里面就可以了。

K个最小和

有 k 个整数数组, 各包含k个元素, 在每一个数组中取出一个元素加起来,可以得到 $k^k$ 个和, 输出最小的和的前 k 项。
这个题,我们可以从简单的考虑起来。假设只有两个数组长度为 n,求出前 k 项最小和。 最简单的算法就是直接把所有组合 push 到 pq 中,然后取出前 k 项即可。 但是这样做的话, 复杂度是 n^2logn^2. 这里,我们提供两种更好的方法。

解法1: 我们各取数组的前 k 个元素, 然后维护一个 k 大小的大端 heap,这样每次 push 的时候,都进行比较。只有比堆顶元素小的时候,我们 pop 堆顶,然后 push 进去。 k^2logk.

class Solution {
    public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
        PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> b[0] + b[1] - a[0] - a[1]);
        for(int i = 0; i < nums1.length && i < k; i++) {
            for(int j = 0; j < nums2.length && j < k; j++) {
                if(pq.size() < k) {
                    pq.offer(new int[]{nums1[i], nums2[j]});
                } else if(pq.size() >= k && ((nums1[i] + nums2[j]) < (pq.peek()[0] + pq.peek()[1]))) {
                    pq.poll();
                    pq.offer(new int[]{nums1[i], nums2[j]});
                }
            }
        }
        List<List<Integer>> ret = new ArrayList<>();
        List<Integer> tmp;
        while(!pq.isEmpty()) {
            int[] cur = pq.poll();
            tmp = new ArrayList<>();
            tmp.add(cur[0]);
            tmp.add(cur[1]);
            ret.add(new ArrayList<>(tmp));
        }
        return ret;                                        
    }
}

解法2:最优解能使算法的复杂度降到 klogk。 我们看以下数列的前两行,我们发现从左到右,从上到下是递增的。 唯有斜对角的关系我们不知道。那么我们可以先把第一列或者第一行的所有和放入 pq 中, 这样我们每取出一个数,在 push 相对应的下一项即可。

$ A_1 + B_1 <= A_1 + B_2 <= A_1 + B_3 … $
$ A_2 + B_1 <= A_2 + B_2 <= A_2 + B_3 … $ .
.
.
$ A_n + B_1 <= A_n + B_2 <= A_n + B_3 … $

class Solution {
    public List<List<Integer>> kSmallestPairs(int[] nums1, int[] nums2, int k) {
        if(nums2.length == 0 || nums1.length == 0) return new ArrayList<>();
        PriorityQueue<int[]> pq = new PriorityQueue<>((a, b) -> a[0] + a[1] - b[0] - b[1]);
        for(int i = 0; i < nums1.length && i < k; i++) {
            pq.offer(new int[]{nums1[i], nums2[0], 0});
        }
        List<List<Integer>> ret = new ArrayList<>();
        int i = 0;
        List<Integer> tmp;
        while(!pq.isEmpty() && i < k) {
            int[] cur = pq.poll();
            tmp = new ArrayList<>();
            tmp.add(cur[0]);
            tmp.add(cur[1]);
            ret.add(tmp);
            if(cur[2] + 1 < k && cur[2] + 1 < nums2.length) pq.offer(new int[]{cur[0], nums2[cur[2] + 1], cur[2] + 1});
            i++;
        }
        return ret;
    }
}
No. title difficulty
42 Merge k Sorted Lists Hard
347 Top K Frequent Elements Medium
218 The Skyline Problem Hard
253 Meeting Rooms II Medium
215 Kth Largest Element in an Array Medium
973 K Closest Points to Origin Medium

Search

    Table of Contents