Interview Questions: Priority Queues

本文介绍了一种动态维护数据集中位数的数据结构,通过使用两个堆实现插入、查找和移除中位数的高效操作。同时,探讨了随机优先队列的实现,包括随机选择和移除元素的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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,使其满足以下条件:

  1. maxHeap.size == minHeap.size 或者 maxHeap.size == minHeap.size + 1

  2. 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));
            }
        }
    }
}

参考

Taxicab.java

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值