基于堆的优先级队列

简介: java自带的优先级队列默认是小顶堆,现在来写一个大顶堆的

 java自带的优先级队列默认是小顶堆,现在来写一个大顶堆的




目录

实现大顶堆的优先级队列:

实现小顶堆的优先级队列:


实现大顶堆的优先级队列:

importjava.util.NoSuchElementException;
classMaxPQ<KeyextendsComparable<Key>> {
privateKey[] pq; // 基于堆的完全二叉树privateintN; // 存储在pq[1..N]中,pq[0]没有使用publicMaxPQ(intmaxN) {
if (maxN>=0) {
pq= (Key[]) newComparable[maxN+1];
        } else {
thrownewIllegalArgumentException("Illegal Capacity: "+maxN);
        }
    }
publicMaxPQ() {
this(10);
    }
publicbooleanisEmpty() {
returnN==0;
    }
publicintsize() {
returnN;
    }
publicvoidinsert(Keyv) {
if (N==pq.length-1)
resize();
pq[++N] =v;
swim(N);
    }
publicKeydelete() { // 大顶堆删除最大的if (isEmpty())
thrownewNoSuchElementException("Priority queue underflow");
Keymax=pq[1]; // 从根节点得到最大元素swap(1, N); // 将其和最后一个结点交换pq[N--] =null; // 便于垃圾回收sink(1); // 恢复堆的有序性returnmax;
    }
privatevoidsink(intk) {
while ((k<<1) <=N) { // j <= N说明一定有左孩子intj=k<<1; // 左孩子下标if (j<N&&less(j, j+1)) // j < N说明一定有右孩子,如果左孩子小于右孩子的值++j; // 下标转移到右孩子if (!less(k, j)) // 如果k结点的值不小于他的孩子中的最大值break; // 已经无法继续下沉了,跳出swap(k, j); // 下沉,和孩子中的最大值交换k=j; // 现在这个元素已经在刚刚的j位置上,继续记录这个元素的位置,看能否继续下沉        }
    }
privatevoidswim(intk) {
// 如果不是第一个元素,并且第k个元素比父节点的值小,那么与父节点交换while (k>1&&less(k>>1, k)) {
swap(k>>1, k);
k>>=1;
        }
    }
privatevoidswap(inti, intk) {
Keyt=pq[i];
pq[i] =pq[k];
pq[k] =t;
    }
privatebooleanless(inti, intk) {
returnpq[i].compareTo(pq[k]) <0;
    }
// ArrayList空参构造方法默认是空数组,第一次插入时会扩容,就比较size+1和10的最大值,扩容后为10privatevoidresize() { // 扩容逻辑简单模仿ArrayListintcapacity=N+ (N>>1);
if (capacity<N) // 如果溢出capacity=N;
if (capacity>Integer.MAX_VALUE-8) {
capacity=hugeCapacity(N);
        }
Key[] temp= (Key[]) newComparable[capacity];
for (inti=1; i<=N; ++i) { // 扩容后复制到新数组temp[i] =pq[i];
        }
pq=temp;
    }
privateinthugeCapacity(intminCapacity) {
if (minCapacity<0) // overflowthrownewOutOfMemoryError();
return (minCapacity>Integer.MAX_VALUE-8) ?Integer.MAX_VALUE : Integer.MAX_VALUE-8;
    }
}
publicclassMaxPriorityQueue {
publicstaticvoidmain(String[] args) {
MaxPQ<Integer>pq=newMaxPQ<Integer>();
pq.insert(5);
pq.insert(124);
pq.insert(456);
pq.insert(678);
pq.insert(2);
pq.insert(2);
pq.insert(6);
pq.insert(3);
pq.insert(88);
pq.insert(-45);
pq.insert(99);
intsize=pq.size();
for (inti=0; i<size; ++i) {
System.out.println(pq.delete());
        }
    }
}

image.gif

运行结果:

678

456

124

99

88

6

5

3

2

2

-45

 

     优先队列由一个基于堆的完全二叉树表示,存储于数组pq[1..N]中,pq[0]没有使用。在insert()中,我们将N加一并把新元素添加在数组最后,然后用swim()恢复堆的有序性(当一颗二叉树的结点都大于等于它的两个子节点时,它被称为堆有序)。在delete()中,我们从pq[1]中得到需要返回的元素,然后将pq[N]移动到pq[1],将N减一,并用sink()恢复堆有序。同时我们还将不再使用的p[N]设置为null,以便系统回收它所占用的空间。


这里的主要逻辑是:


插入元素insert():我们将新元素加到数组末尾,增加堆的大小并让这个新元素上浮到合适的位置。


删除最大元素delete():我们从数组顶端删去最大元素pq[1],就是先将数组的最后一个元素和顶端元素pq[1]交换,然后减小堆的大小N--(即删除数组最后一个元素),并让顶端元素下沉到合适的位置。


同理可得:


实现小顶堆的优先级队列:

import java.util.NoSuchElementException;
class MinPQ<Key extends Comparable<Key>> {
    private Key[] pq; // 基于堆的完全二叉树
    private int N; // 存储在pq[1..N]中,pq[0]没有使用
    public MinPQ(int maxN) {
        if (maxN >= 0) {
            pq = (Key[]) new Comparable[maxN + 1];
        } else {
            throw new IllegalArgumentException("Illegal Capacity: " + maxN);
        }
    }
    public MinPQ() {
        this(10);
    }
    public boolean isEmpty() {
        return N == 0;
    }
    public int size() {
        return N;
    }
    public void insert(Key v) {
        if (N == pq.length - 1)
            resize();
        pq[++N] = v;
        swim(N);
    }
    public Key delete() { // 大顶堆删除最大的
        if (isEmpty())
            throw new NoSuchElementException("Priority queue underflow");
        Key max = pq[1]; // 从根节点得到最小元素
        swap(1, N); // 将其和最后一个结点交换
        pq[N--] = null; // 便于垃圾回收
        sink(1); // 恢复堆的有序性
        return max;
    }
    private void sink(int k) {
        while ((k << 1) <= N) { // j <= N说明一定有左孩子
            int j = k << 1; // 左孩子下标
            if (j < N && greater(j, j + 1)) // j < N说明一定有右孩子,如果左孩子大于右孩子的值
                ++j; // 下标转移到右孩子
            if (!greater(k, j)) // 如果k结点的值不大于他的孩子中的最大值
                break; // 已经无法继续下沉了,跳出
            swap(k, j); // 下沉,和孩子中的最小值交换
            k = j; // 现在这个元素已经在刚刚的j位置上,继续记录这个元素的位置,看能否继续下沉
        }
    }
    private void swim(int k) {
        // 如果不是第一个元素,并且第k个元素比父节点的值小,那么与父节点交换
        while (k > 1 && greater(k >> 1, k)) {
            swap(k >> 1, k);
            k >>= 1;
        }
    }
    private void swap(int i, int k) {
        Key t = pq[i];
        pq[i] = pq[k];
        pq[k] = t;
    }
    private boolean greater(int i, int k) {
        return pq[i].compareTo(pq[k]) > 0;
    }
    // ArrayList空参构造方法默认是空数组,第一次插入时会扩容,就比较size+1和10的最大值,扩容后为10
    private void resize() { // 扩容逻辑简单模仿ArrayList
        int capacity = N + (N >> 1);
        if (capacity < N) // 如果溢出
            capacity = N;
        if (capacity > Integer.MAX_VALUE - 8) {
            capacity = hugeCapacity(N);
        }
        Key[] temp = (Key[]) new Comparable[capacity];
        for (int i = 1; i <= N; ++i) { // 扩容后复制到新数组
            temp[i] = pq[i];
        }
        pq = temp;
    }
    private int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > Integer.MAX_VALUE - 8) ? Integer.MAX_VALUE : Integer.MAX_VALUE - 8;
    }
}
public class MinPriorityQueue {
    public static void main(String[] args) {
        MinPQ<Integer> pq = new MinPQ<Integer>();
        pq.insert(5);
        pq.insert(124);
        pq.insert(456);
        pq.insert(678);
        pq.insert(2);
        pq.insert(2);
        pq.insert(6);
        pq.insert(3);
        pq.insert(88);
        pq.insert(-45);
        pq.insert(99);
        int size = pq.size();
        for (int i = 0; i < size; ++i) {
            System.out.println(pq.delete());
        }
    }
}

image.gif

运行结果:

-45

2

2

3

5

6

88

99

124

456

678

 

其实相对于大顶堆的优先级队列就只将less改为了greater。


==========================Talk is cheap, show me the code========================

目录
相关文章
|
JSON Java API
LAZADA平台API文档示例
LAZADA平台API文档示例
|
3月前
|
人工智能 JavaScript 前端开发
Geo专家于磊:Json-LD优化实战SOP与双核四驱体系
本文探讨生成式引擎优化(GEO)时代JSON-LD的核心作用,提出于磊老师首创的“两大核心(人性化Geo+交叉验证)+四轮驱动”方法论,详解JSON-LD结构化标记、E-E-A-T强化、权威引用等实战SOP,助力内容获AI精准理解与高信度引用。
190 5
|
4月前
|
人工智能 监控 机器人
将Vibe Coding从耗神低效转为高效可靠
本文提出“AI自助闭环”工作流:通过构建CLI模拟测试平台+TDD+预设问题清单重构,在无需人工干预下实现AI自主编写、测试、修复与优化代码。核心是让AI获得即时反馈能力,将Vibe Coding从耗神低效转为高效可靠。
将Vibe Coding从耗神低效转为高效可靠
|
6月前
|
人工智能 自然语言处理 安全
2026年新手零门槛解锁OpenClaw/Clawdbot部署+WhatsApp接入教程指南
在AI自动化工具全面普及的2026年,OpenClaw(原Clawdbot、Moltbot)凭借“自然语言指令驱动+全场景任务自动执行”的核心优势,成为个人、跨境从业者及轻量团队的必备智能助手——它无需专业编程基础,无需手动配置复杂运行环境,就能轻松实现文件管理、联网搜索、跨境信息同步、批量消息推送等多元化操作,完美适配WhatsApp的高频使用场景。而阿里云推出的OpenClaw一键部署方案,依托云端基础设施的稳定性与自动化部署能力,预置专属优化镜像、整合所有核心依赖,彻底打破了新手的技术门槛,哪怕你完全不懂服务器、不懂代码,跟着步骤15-20分钟就能完成部署,部署后通过简单配置即可快速接入
811 2
|
11月前
|
机器学习/深度学习 人工智能 测试技术
EdgeMark:嵌入式人工智能工具的自动化与基准测试系统——论文阅读
EdgeMark是一个面向嵌入式AI的自动化部署与基准测试系统,支持TensorFlow Lite Micro、Edge Impulse等主流工具,通过模块化架构实现模型生成、优化、转换与部署全流程自动化,并提供跨平台性能对比,助力开发者在资源受限设备上高效选择与部署AI模型。
848 9
EdgeMark:嵌入式人工智能工具的自动化与基准测试系统——论文阅读
|
弹性计算 编解码 算法
央视、芒果TV、浙江、江苏,阿里云支持多家跨年晚会全球直播!
央视、芒果TV、浙江、江苏,阿里云支持多家跨年晚会全球直播!
351 2
|
机器学习/深度学习 数据可视化 数据挖掘
PyTorch Geometric (PyG) 入门教程
PyTorch Geometric是PyTorch1的几何图形学深度学习扩展库。本文旨在通过介绍PyTorch Geometric(PyG)中常用的方法等内容,为新手提供一个PyG的入门教程。
PyTorch Geometric (PyG) 入门教程
|
机器学习/深度学习 算法 自动驾驶
深度学习之分布式智能体学习
基于深度学习的分布式智能体学习是一种针对多智能体系统的机器学习方法,旨在通过多个智能体协作、分布式决策和学习来解决复杂任务。这种方法特别适用于具有大规模数据、分散计算资源、或需要智能体彼此交互的应用场景。
1066 4
|
网络架构
Ensp DHCP 接口地址池(配置命令)
Ensp DHCP 接口地址池(配置命令)
681 1
|
机器学习/深度学习 存储 人工智能
AI 辅助测试(MEAP)(一)(1)
AI 辅助测试(MEAP)(一)
521 0