Priority Queues
Dynamic median. Design a data type that supports insert in logarithmic time, find-the-median in constant time, and remove-the-median in logarithmic time. If the number of keys in the data type is even, find/remove the lower median.
维护两个堆,一个大顶堆maxHeap和一个小顶堆minHeap,使其满足以下条件:
-
maxHeap.size == minHeap.size 或者 maxHeap.size == minHeap.size + 1
-
minHeap中的任意元素都比maxHeap中的任意元素大
这样将所有元素分为数量相等(或差1)的大小堆,使得无论元素总数是偶数还是奇数,中位数都是maxHeap的堆顶元素。
/*
abstract class Heap {
protected int size;
protected int[] heap;
public Heap() {
heap = new int[2];
}
public boolean isEmpty() {
return size == 0;
}
public void insert(int n) {
if (size == heap.length) {
resize(2 * heap.length);
}
heap[size++] = n;
swim(size - 1);
}
public int peek() {
return heap[0];
}
public int remove() {
int temp = heap[0];
exch(0, --size);
if (size > 0 && size == heap.length / 4) {
resize(heap.length / 2);
}
sink(0);
return temp;
}
abstract boolean compare(int i, int j);
protected void swim(int k) {
while (k > 0 && compare(k / 2, k)) {
exch(k / 2, k);
k /= 2;
}
}
protected void sink(int k) {
while (2 * k <= size - 1) {
int j = 2 * k;
if (j < size - 1 && compare(j, j + 1)) {
j++;
}
if (!compare(k, j)) {
break;
}
exch(k, j);
k = j;
}
}
protected void resize(int capacity) {
int[] temp = new int[capacity];
for (int i = 0; i < heap.length; i++) {
temp[i] = heap[i];
}
heap = temp;
}
protected void exch(int i, int j) {
int temp = heap[i];
heap[i] = heap[j];
heap[j] = temp;
}
}
class MaxHeap extends Heap {
@Override
boolean compare(int i, int j) {
return heap[i] < heap[j];
}
}
class MinHeap extends Heap {
@Override
boolean compare(int i, int j) {
return heap[i] > heap[j];
}
}
*/
public class DynamicMedian {
MaxHeap maxHeap;
MinHeap minHeap;
public DynamicMedian() {
maxHeap = new MaxHeap();
minHeap = new MinHeap();
}
public void insert(int n) {
// 依照大小切分元素
if (maxHeap.isEmpty()) {
maxHeap.insert(n);
} else {
if (n > maxHeap.peek()) {
minHeap.insert(n);
} else {
maxHeap.insert(n);
}
}
// 使大小两堆的个数满足要求
if (maxHeap.size + 1 == minHeap.size) {
maxHeap.insert(minHeap.remove());
}
if (maxHeap.size == minHeap.size + 2) {
minHeap.insert(maxHeap.remove());
}
}
public int findMedian() {
return maxHeap.peek();
}
public int removeMedian() {
return maxHeap.remove();
}
}
Randomized priority queue. Describe how to add the methods ??????() and ?????????() to our binary heap implementation. The two methods return a key that is chosen uniformly at random among the remaining keys, with the latter method also removing that key. The ??????() method should take constant time; the ?????????() method should take logarithmic time. Do not worry about resizing the underlying array.
删除随机元素的方法:将随机元素与末尾元素交换,使size减1,并从随机位置开始执行一次sink()。
import java.util.Random;
public class RandomizedPQ {
private int[] pq;
private int size;
Random random;
public RandomizedPQ(int[] pq) {
this.pq = pq;
random = new Random();
}
public boolean isEmpty() {
return size == 0;
}
public void insert(int n) {
if (size == pq.length) {
resize(2 * size);
}
pq[size++] = n;
swim(size - 1);
}
public int sample() {
int r = random.nextInt(size);
return pq[r];
}
public int delRandom() {
int r = random.nextInt(size);
int temp = pq[r];
exch(r, --size);
if (size > 0 && size == pq.length / 4) {
resize(pq.length / 2);
}
sink(r);
return temp;
}
private void swim(int k) {
while (k > 0 && pq[k / 2] < pq[k]) {
exch(k / 2, k);
k /= 2;
}
}
private void sink(int k) {
while (2 * k < size) {
int j = 2 * k;
if (j < size - 1 && pq[j] < pq[j + 1]) {
j++;
}
if (pq[k] >= pq[j]) {
break;
}
exch(k, j);
k = j;
}
}
private void exch(int i, int j) {
int temp = pq[i];
pq[i] = pq[j];
pq[j] = temp;
}
private void resize(int capacity) {
int[] temp = new int[capacity];
for (int i = 0; i < size; i++) {
temp[i] = pq[i];
}
pq = temp;
}
}
Taxicab numbers. A taxicab number is an integer that can be expressed as the sum of two cubes of positive integers in two different ways: a3+b3=c3+d3. For example, 1729 is the smallest taxicab number: 93+103=13+123. Design an algorithm to find all taxicab numbers with a, b, c, and d less than n.
- Version 1: Use time proportional to n2logn and space proportional to n2.
- Version 2: Use time proportional to n2logn and space proportional to n.
Version 1:将所有的(i, j, sum)对(共n2个)保存到优先队列中,依次取堆顶元素与之前的堆顶元素比较,如果相同,说明找到一个Texicab Number。空间复杂度为O(N2)。
Version 2:当i不变,j变化时,构成的(i, j, sum)对两两之间不可能相等,即由i、j构成的 n x n 阶立方和矩阵中,每一行中都不可能存在两个相同的sum,只有不同行间才可能存在相同的sum。所以使用优先队列时,只要为每一行分配一个位置即可。空间复杂度为O(N)。
import java.util.PriorityQueue;
import java.util.Scanner;
class CubeSum implements Comparable<CubeSum> {
int i;
int j;
long sum;
public CubeSum(int i, int j) {
this.i = i;
this.j = j;
this.sum = i * i * i + j * j * j;
}
@Override
public int compareTo(CubeSum that) {
if (this.sum < that.sum) {
return -1;
} else if (this.sum > that.sum) {
return 1;
} else if (this.i < that.i) {
return -1;
} else if (this.i > that.i) {
return 1;
} else {
return 0;
}
}
@Override
public String toString() {
return i + "^3 + " + j + "^3";
}
}
public class Taxicab_Version1 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
PriorityQueue<CubeSum> pq = new PriorityQueue<>();
// 将所有组合都存入优先队列
for (int i = 1; i <= n; i++) {
for (int j = i; j <= n; j++) {
pq.offer(new CubeSum(i, j));
}
}
int count = 1; // 记录出现次数,当且仅当出现2此时才输出
CubeSum prev = new CubeSum(0, 0);
while (!pq.isEmpty()) {
CubeSum cur = pq.remove();
if (cur.sum == prev.sum) {
count++;
if (count == 2) {
System.out.println(cur.sum + " = " + prev + " = " + cur);
}
} else {
count = 1;
}
prev = cur;
}
}
}
public class Taxicab_Version2 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
PriorityQueue<CubeSum> pq = new PriorityQueue<>();
// 只为每一行分配一个位置
for (int i = 1; i <= n; i++) {
pq.offer(new CubeSum(i, i));
}
int count = 1; // 记录出现次数,当且仅当出现2此时才输出
CubeSum prev = new CubeSum(0, 0);
while (!pq.isEmpty()) {
CubeSum cur = pq.remove();
if (cur.sum == prev.sum) {
count++;
if (count == 2) {
System.out.println(cur.sum + " = " + prev + " = " + cur);
}
} else {
count = 1;
}
prev = cur;
if (cur.j < n) {
pq.offer(new CubeSum(cur.i, cur.j + 1));
}
}
}
}
参考