了解Java的PriorityQueue吗?

PriorityQueue 是 JDK 1.5 引入的一个重要集合类,它实现了一个基于优先级的队列。与普通的 Queue 不同,PriorityQueue 中元素的出队顺序由元素的优先级决定,即优先级最高的元素会最先出队。本文将详细探讨 PriorityQueue 的实现细节、常见应用场景以及与其他集合类的对比。

1. PriorityQueue 的实现原理

PriorityQueue 是基于二叉堆(Binary Heap)实现的,而二叉堆是一种完全二叉树。PriorityQueue 通过堆元素的上浮和下沉操作,确保插入和删除操作的时间复杂度为 (O(\log n))。

1.1 底层数据结构

PriorityQueue 底层使用一个可变长的数组来存储数据。初始容量为 11,当需要更大的容量时,会按需扩容。

java

private static final int DEFAULT_INITIAL_CAPACITY = 11;
transient Object[] queue; // 存储元素的数组
private int size = 0; // 队列中元素的数量
1.2 插入元素

插入元素时,PriorityQueue 会先将元素添加到数组的末尾,然后通过上浮操作(sift up)将其移动到合适的位置,以保持堆的性质。

java

public boolean offer(E e) {
    if (e == null)
        throw new NullPointerException();
    int i = size;
    if (i >= queue.length)
        grow(i + 1);
    size = i + 1;
    if (i == 0)
        queue[0] = e;
    else
        siftUp(i, e);
    return true;
}

private void siftUp(int k, E x) {
    if (comparator != null)
        siftUpUsingComparator(k, x);
    else
        siftUpComparable(k, x);
}

private void siftUpComparable(int k, E x) {
    Comparable<? super E> key = (Comparable<? super E>) x;
    while (k > 0) {
        int parent = (k - 1) >>> 1;
        Object e = queue[parent];
        if (key.compareTo((E) e) >= 0)
            break;
        queue[k] = e;
        k = parent;
    }
    queue[k] = key;
}
1.3 删除堆顶元素

删除堆顶元素时,PriorityQueue 会将堆顶元素与数组的最后一个元素交换,然后通过下沉操作(sift down)将交换后的元素移动到合适的位置,以保持堆的性质。

java

public E poll() {
    if (size == 0)
        return null;
    int s = --size;
    E result = (E) queue[0];
    E x = (E) queue[s];
    queue[s] = null;
    if (s != 0)
        siftDown(0, x);
    return result;
}

private void siftDown(int k, E x) {
    if (comparator != null)
        siftDownUsingComparator(k, x);
    else
        siftDownComparable(k, x);
}

private void siftDownComparable(int k, E x) {
    Comparable<? super E> key = (Comparable<? super E>) x;
    int half = size >>> 1;
    while (k < half) {
        int child = (k << 1) + 1;
        Object c = queue[child];
        int right = child + 1;
        if (right < size && ((Comparable<? super E>) c).compareTo((E) queue[right]) > 0)
            c = queue[child = right];
        if (key.compareTo((E) c) <= 0)
            break;
        queue[k] = c;
        k = child;
    }
    queue[k] = key;
}
2. 常见应用场景

PriorityQueue 在许多算法和应用中都有广泛的应用,以下是几个经典的场景:

2.1 堆排序

堆排序是一种基于堆数据结构的排序算法,其时间复杂度为 (O(n \log n))。PriorityQueue 可以方便地用于实现堆排序:

java

public static <E> List<E> heapSort(Collection<E> collection) {
    PriorityQueue<E> heap = new PriorityQueue<>(collection);
    List<E> sortedList = new ArrayList<>();
    while (!heap.isEmpty()) {
        sortedList.add(heap.poll());
    }
    return sortedList;
}
2.2 求第 K 大的数

求第 K 大的数是一个常见的面试题,利用 PriorityQueue 可以高效地解决这个问题:

java

public static int findKthLargest(int[] nums, int k) {
    PriorityQueue<Integer> minHeap = new PriorityQueue<>(k);
    for (int num : nums) {
        if (minHeap.size() < k) {
            minHeap.offer(num);
        } else if (num > minHeap.peek()) {
            minHeap.poll();
            minHeap.offer(num);
        }
    }
    return minHeap.peek();
}
2.3 带权图的遍历

在带权图的遍历算法(如 Dijkstra 算法)中,PriorityQueue 可以用于高效地选择当前距离最近的顶点:

java

public static Map<Vertex, Integer> dijkstra(Graph graph, Vertex source) {
    PriorityQueue<Vertex> priorityQueue = new PriorityQueue<>(Comparator.comparingInt(Vertex::getDistance));
    source.setDistance(0);
    priorityQueue.add(source);
    Map<Vertex, Integer> distances = new HashMap<>();
    while (!priorityQueue.isEmpty()) {
        Vertex current = priorityQueue.poll();
        for (Edge edge : current.getEdges()) {
            Vertex neighbor = edge.getTarget();
            int newDist = current.getDistance() + edge.getWeight();
            if (newDist < neighbor.getDistance()) {
                neighbor.setDistance(newDist);
                priorityQueue.add(neighbor);
                distances.put(neighbor, newDist);
            }
        }
    }
    return distances;
}
3. 注意事项
  • PriorityQueue 是非线程安全的,如果需要在多线程环境中使用,请考虑使用 PriorityBlockingQueue
  • PriorityQueue 不支持存储 NULL 元素。
  • PriorityQueue 默认是小顶堆,如果需要自定义优先级顺序,可以提供一个 Comparator
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值