硬核!手写一个优先队列

简介: 优先队列的原理是借助堆,通过本文图解让你更细致了解优先队列。

文章收录在首发公众号:bigsai 期待你的到访!

前言

事情还要从一个故事讲起:
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

对于上面那只可爱的小狗狗不会,本篇即为该教程,首先,我要告诉这只可爱的小狗狗,这种问题你要使用的数据结构为优先队列,每次操作的时间复杂度为O(logn),而整个过程的时间复杂度为O(nlogn).

对于本片的设计与实现和堆排序可能有些相似,因为他们都借助堆来实现算法和数据结构,下面详细介绍优先队列的设计与实现

而堆就是一类特殊的数据结构的统称。堆通常是一个可以被看做一棵树(完全)的数组对象。且总是满足以下规则:

  • 堆总是一棵完全二叉树
  • 每个节点总是大于(或小于)它的孩子节点。

对于完全二叉树,我想大家都能明白,就是最底层叶子节点要严格按照从左向右来。
在这里插入图片描述
堆有大根堆和小根堆,如果是所有父节点都大于子节点的时候,那么这就是个大根堆,反之则为小根堆,以下就是一个大根堆:
在这里插入图片描述
最后需要注意的是我们并不是用链式去储存这个二叉树而是用数组去存储这个树,虽然链式的使用场景可能更多一些,但是在完全二叉树的情况下空间使用率较好没有斜树的出现。并且在操作的时候可以直接通过编号找到位置进行交换。

优先队列

如何理解优先队列,我们先从一段对话说起:
在这里插入图片描述

优先队列,它是一个队列。而普通的队列遵从先进先出的规则。而优先队列遵循一个排序的规则:每次抛出自定义排序最大(小)的,默认的情况是抛出最小的,本篇也就从最基本的原理进行分析。

并且它的用法队列还是一样的,,所以我们在设计这个类的时候api方面要与队列的api一致。
在这里插入图片描述

我们主要实现add、poll、和peek方法,并且会着重于算法的实现而不太着重一些细节的实现。

虽然优先队列和堆排序利用堆结构特性的流程有一些相似,但是两者其实还是有些操作上的区别的:

堆排序

  • 刚开始是一个杂乱无章的序列,所以需要将杂乱的序列(树)通过一个方法变成一个合法的堆。
  • 转成一个堆之后需要删除n次每次删除完都要重新调整这个堆。没有插入操作

优先队列

  • 队列(堆)刚开始的内容为空,每次增加一个元素时需要即使调整堆。每次删除也要及时调整堆,增加和删除每次都只是一个元素。

但是优先队列的具体操作流程是如何的呢?我们具体分析其插入和删除的流程

插入add流程(小根堆为例):

  • 正常处理完的优先队列内的数据满足一个堆的结构,所以就是插入在堆中。
  • 堆是一棵完全二叉树,所以在插入初始,插入到最后一个位置不影响其他结构。
  • 节点和父节点比较大小(父节点索引为其二分之一)。如果该节点比父节点更小,则交换数据,一直到不能交换为止,这个过程不用担心不合法,因为父节点更小的话更满足比孩子节点更小。

在这里插入图片描述

删除pop流程(小根堆为例):

  • pop删除操作取优先队列内最小的元素,而这个元素肯定就是堆顶元素了,取完之后,这个堆的其他部分还是满足堆的结构但是缺少堆顶。
  • 为了不影响整个结构,我们将末尾的那个元素移到堆顶,此时堆需要调整使其满足堆的性质条件。
  • 交换的这个节点和左右孩子进行比较,如果需要交换则交换,交换后再次考虑交换子节点是否需要交换,一直到不交换为止。最坏情况是交换到根节点,这个复杂度每次为O(logn).

在这里插入图片描述

代码实现

我想到这里,优先队列的内部流程思想你已经掌握了,但是懂优先队列原理和手写优先队列是两个概念,为了更深入的学习优先队列,在这里就带你手写一个简易型的优先队列。

在代码的具体实现方面,最主要的就是pop()和add()两个函数了。在pop()函数具体实现的时候,将最后一个元素移到堆头考虑和其他孩子节点交换的时候,用while进行操作的时候计算孩子下标的时候要确保不越界。我们用的是数组存储数据,优先队列的长度不一定等于这个数组的长度

而在实现add()函数的时候,这里简单的考虑了一下扩容。

具体实现的代码为:

import java.util.Arrays;

public class priQueue {

    private  int size;//优先队列的大小
    private  int capacity;//数组的容量
    private  int value[];//储存的值

    public priQueue() {
        this.capacity = 10;
        this.value = new int[capacity];
        this.size=0;
    }
    public priQueue(int capacity) {
        this.capacity = capacity;
        this.value = new int[capacity];
        this.size=0;
    }

    /**
     * 插入元素
     * @param number
     */
    public void add(int number) {
        if(size==capacity)//扩容
        {
            capacity*=1.5;
            value= Arrays.copyOf(value,capacity);
        }
        value[size++]=number;//先加到末尾
        int index=size-1;
        while (index>=1) {//进行交换
            if(value[index]<value[index/2]) {
                swap(index,index/2,value);
                index=index/2;
            }
            else//不需要交换即停止
                break;
        }
    }
    public int peek() {
        return  value[0];
    }

    /**
     * 抛出队头
     * @return
     */
    public int pop() {
        int val=value[0];//呆返回数据额
        value[0]=value[--size];//将最后一个元素赋值在堆头
        int index=0,leftChild=0,rightChild=0;
        while (true)
        {
            leftChild=index*2+1;
            rightChild=index*2+2;
            if(leftChild>=size)//左孩子必须满足在条件内
                break;
            else if(rightChild<size&&value[rightChild]<value[index]&&value[rightChild]<value[leftChild])
            {//右孩子更小
                swap(index,rightChild,value);
                index=rightChild;
            }
            else if(value[leftChild]<value[index])
            {//左孩子更小
                swap(index,leftChild,value);
                index=leftChild;
            }
            else //不需要 它自己最小
                break;
        }
        return  val;
    }
    //交换两个元素
    public  void swap(int i,int j,int arr[]) {
        int team=arr[i];
        arr[i]=arr[j];
        arr[j]=team;
    }

    public int size() {
        return  size;
    }
}

写个类测试一下看看:

在这里插入图片描述

结语

在这里插入图片描述

本次优先队列介绍就到这里啦,感觉不错记得点赞或一键三连哦,建议和堆排序一起看和学习效果更佳,要能够手写代码。个人公众号:bigsai 回复 bigsai 更多精彩和资源与你分享。
在这里插入图片描述

目录
相关文章
Anaconda在开始菜单找不到Anaconda command prompt入口
这篇文章提供了解决Anaconda安装后在开始菜单找不到Anaconda command prompt入口问题的步骤,通过运行命令`python .\\Lib\_nsis.py mkmenus`重新创建Anaconda的开始菜单快捷方式。
Anaconda在开始菜单找不到Anaconda command prompt入口
|
传感器 数据可视化 JavaScript
状态机(State Machines):理解、设计和应用有限状态机
状态机(State Machines)是一种强大的计算模型和设计工具,用于建模和控制有限状态的系统和行为。无论是在软件开发、自动化控制、游戏设计还是其他领域,状态机都发挥着关键作用。本博客将深入探讨状态机的概念、工作原理以及如何在不同应用中设计和应用它们。
10449 78
|
定位技术 Python Windows
彻底卸载并重装Anaconda环境与Python的方法
彻底卸载并重装Anaconda环境与Python的方法
8374 1
|
4天前
|
人工智能 自然语言处理 JavaScript
2026年Windows+Ollama本地部署OpenClaw保姆级教程:本地AI Agent+阿里云上快速搭建
2026年OpenClaw凭借本地部署、私有化运行的特性,成为打造个人智能体的核心工具,而Ollama作为轻量级本地大模型管理工具,能让OpenClaw摆脱对云端大模型的依赖,实现**本地推理、数据不泄露、全流程私有化**的智能体验。本文基于Windows 11系统,从硬件环境准备、Ollama安装与模型定制、OpenClaw部署配置、技能扩展到常见问题排查,打造保姆级本地部署教程,同时补充阿里云OpenClaw(Clawdbot)快速部署步骤,兼顾本地私有化需求与云端7×24小时运行需求,文中所有代码命令均可直接复制执行,确保零基础用户也能快速搭建属于自己的本地智能体。
6097 33
|
1天前
|
人工智能 安全
CoPaw:3分钟部署你的 AI助理
源自阿里巴巴开源生态的个人 AI 助理——CoPaw。作为阿里倾力打造的开源力作,CoPaw 完美打通钉钉、飞书、Discord 等多平台对话通道,支持定时任务自动化。内置 PDF/Office 深度处理、新闻摘要等强大技能,更开放自定义扩展接口。坚持数据全程私有化部署,绝不上传云端,让每一位用户都能在大厂技术加持下,拥有安全、专属的智能助手。
|
5天前
|
人工智能 JSON JavaScript
手把手教你用 OpenClaw + 飞书,打造专属 AI 机器人
手把手教你用 OpenClaw(v2026.2.22-2)+ 飞书,10分钟零代码搭建专属AI机器人!内置飞书插件,无需额外安装;支持Claude等主流模型,命令行一键配置。告别复杂开发,像聊同事一样自然对话。
2729 8
手把手教你用 OpenClaw + 飞书,打造专属 AI 机器人
|
3天前
|
人工智能 自然语言处理 机器人
保姆级教程:Mac本地搭建OpenClaw及阿里云上1分钟部署OpenClaw+飞书集成实战指南
OpenClaw(曾用名Clawdbot、Moltbot)作为2026年最热门的开源个人AI助手平台,以“自然语言驱动自动化”为核心,支持对接飞书、Telegram等主流通讯工具,可替代人工完成文件操作、日历管理、邮件处理等重复性工作。其模块化架构适配多系统环境,既可以在Mac上本地化部署打造私人助手,也能通过阿里云实现7×24小时稳定运行,完美兼顾隐私性与便捷性。
1985 4
|
11天前
|
存储 人工智能 负载均衡
阿里云OpenClaw多Agent实战宝典:从极速部署到AI团队搭建,一个人=一支高效军团
在AI自动化时代,单一Agent的“全能模式”早已无法满足复杂任务需求——记忆臃肿导致响应迟缓、上下文污染引发逻辑冲突、无关信息加载造成Token浪费,这些痛点让OpenClaw的潜力大打折扣。而多Agent架构的出现,彻底改变了这一现状:通过“单Gateway+多分身”模式,让一个Bot在不同场景下切换独立“大脑”,如同组建一支分工明确的AI团队,实现创意、写作、编码、数据分析等任务的高效协同。
4778 31
|
3天前
|
人工智能 数据可视化 安全
Claude Code小白邪修指南:一键安装+语音增效,附阿里云极速部署OpenClaw/Clawdbot攻略
对于AI工具新手而言,Claude Code的原生安装流程繁琐、终端操作门槛高,让不少人望而却步。但2026年的今天,“邪修”玩法彻底打破这一壁垒——通过开源工具实现一键部署,用语音交互提升3-4倍效率,再搭配阿里云OpenClaw的稳定运行环境,让小白也能快速上手AI编程助手。本文将详解“邪修”核心技巧、语音增效方案,以及阿里云OpenClaw部署步骤,附带完整配置代码与避坑指南,帮助你轻松开启AI辅助工作新模式。
1358 0

热门文章

最新文章