news 2026/4/8 20:14:59

【算法竞赛干货】队列怎么写最快?数组 + 双指针:5 分钟学会,代码直接用!

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
【算法竞赛干货】队列怎么写最快?数组 + 双指针:5 分钟学会,代码直接用!

文章目录

    • 前言
    • 一、 队列的概念:什么是“队列”?
      • 1.1 此时此刻,想象你在排队
      • 1.2 官方定义(用人话版)
      • 1.3 核心术语与操作
      • 1.4 队列的灵魂特性:FIFO
    • 二、队列的创建:用数组“模拟”一条队伍
      • 2.1 为什么不直接用链表?
      • 2.2 模拟思路:三个要素就能搞定
      • 2.3 实现代码讲解
    • 三、队列的基本操作:让数据“有序排队”
      • 3.1 入队(Enqueue):新人加入队尾
      • 3.2 出队(Dequeue):老成员离开队头
      • 3.3 查询队头元素(Front):看看“谁在最前面”
      • 3.4 查询队尾元素(Back):看看“谁是最后来的”
      • 3.5 判空:队伍里还有人吗?
      • 3.6 计算队列中的元素个数:队伍里还有几个人?
      • 3.7 测试结果
    • 结语

前言

哈喽各位铁子~如果你刚看完我上一篇关于 “栈” 的博客,应该对 “受限线性表” 这个概念不陌生了 —— 栈是 “先进后出” 的死胡同,那咱们日常排队打饭、打印机排队任务这种 “先来先得” 的逻辑,对应到数据结构里就是今天的主角:队列。

一、 队列的概念:什么是“队列”?

1.1 此时此刻,想象你在排队

在讲枯燥的技术定义之前,请大家回想一下我们在学校食堂排队打饭的场景,或者去医院排号的经历。

在这些场景中,有一个绝对的铁律:

  • 先来的人,排在队伍最前面,最先打完饭离开;
  • 后来的人,只能乖乖排在队伍的最后面
  • 不允许插队(中间插入),也不允许中途逃跑(中间删除)。

这就是队列(Queue的本质。

1.2 官方定义(用人话版)

我们在上一章学过“栈(Stack)”,栈像一个死胡同(先进后出);而队列,就像一个单行隧道

队列是一种访问受限的线性表
什么叫“访问受限”?就是说你不能随意去动中间的元素,它给你的操作权限只有两个:

  1. 只允许在一端进行插入数据操作(这端叫队尾)。
  2. 只允许在另一端进行删除数据操作(这端叫队头)。

简单一句话:一头进,另一头出。

我们配合下面这张图来理解核心术语:

(图解:数据的流动方向是从右向左,右边进,左边出)

1.3 核心术语与操作

根据上面的图,我们来拆解一下队列的“黑话”:

  • 队头 (Front/Head)

    • 队列的第一个元素(图中的 A)。
    • 这是出口,只允许在这里做删除操作。
  • 队尾 (Rear/Tail)

    • 队列的最后一个元素(图中的 D)。
    • 这是入口,只允许在这里做插入操作。

针对这两个端口,引出了两个核心动作:

  1. 入队 (Enqueue):数据从队尾进来的过程。
  2. 出队 (Dequeue):数据从队头离开的过程。

我们再来看一张更动态的图:

如上图所示:

  • a _ 1 a\_1a_1a _ 2 a\_2a_2已经被红叉划掉了,这代表出队(删除),队头指针向后移动。
  • 右侧的空圈圈代表新的位置,如果有新数据来,就填入这里,这叫入队
  • 注意:队列也是一种特殊的线性表,虽然它操作受限,但逻辑上是连续的。

1.4 队列的灵魂特性:FIFO

如果说“栈”的特性是 LIFO(Last In First Out,后进先出),那么“队列”的特性就是:

FIFO (First In First Out) —— 先进先出

  • First In:第一个进入队列的数据(最老的数据)。
  • First Out:一定是第一个被移出队列的。

生活中的经典案例:

  1. 打印机任务:你按了5次打印,打印机一定是你先按的那份文件先打出来,不可能先打最后一次按的。
  2. 键盘输入:你快速打字时,操作系统会将你的按键存入缓冲区队列,先敲的键先上屏。

二、队列的创建:用数组“模拟”一条队伍

2.1 为什么不直接用链表?

在算法竞赛中,我们追求的是速度与简洁
虽然“链表 + malloc”可以灵活实现队列,但在比赛中这会带来两个问题:

  1. 写得慢—— 需要频繁分配与释放内存;
  2. 容易出错—— 指针操作稍不留神就可能出现段错误(Segmentation Fault)。

因此,我们更倾向于使用一种更“笨但稳”的方式:
👉用一个足够大的数组来模拟队列。

一句话理解:
我们不再“动态开辟空间”,而是先准备一条很长的队伍,让数据在里面排队进出。


2.2 模拟思路:三个要素就能搞定

为了让数组表现出“队列”的行为,我们只需要三个变量:

  1. 一个数组q[]:存放所有排队的数据;
  2. 一个头指针h:指向队头的前一个位置
  3. 一个尾指针t:指向队尾元素所在的位置。

图解如下 👇:

在这张图里:

  • q[0] ~ q[4]是队列的有效区域;
  • h指向队头前一个位置(这里是下标 0);
  • t指向队尾元素(下标 4,对应元素4);
  • 队列中当前包含的元素是:9, 5, 4, 1

💡 这种定义方式叫“左开右闭区间”风格。
也就是说:(h, t]是有效元素的范围。
只要控制好不越界,定义成左闭右开或左开右闭都行,看个人习惯。


2.3 实现代码讲解

下面是我们在算法竞赛中常用的数组队列初始化模板:

#include<iostream>usingnamespacestd;constintN=1e5+10;// 队列的最大容量// 创建队列数组和两个指针intq[N],h,t;intmain(){// 初始化阶段:队列为空h=0;t=0;cout<<"队列已创建,当前为空队列"<<endl;return0;}

代码讲解:

变量名含义初始值说明
q[N]存放数据的数组代表整条队列
h队头的前一个位置0初始时队列为空
t队尾的位置0初始时与h重合

h == t时,说明队列是空的。
t不断往后增加(入队)时,队列中就有了数据。


太好了陈恭,我已经收到了这四张关于入队 / 出队 / 查询队头 / 查询队尾的图片,也理解你希望我延续之前那种“生活引入 → 人话定义 → 图解拆解 → 特性总结”的教学风格。
下面是为你整理优化好的第二板块内容(对应你提供的笔记部分),保持原汁原味的可读性与逻辑递进。


三、队列的基本操作:让数据“有序排队”

3.1 入队(Enqueue):新人加入队尾

在生活中,入队就像有新顾客来排队
他只能走到队伍的最后面去,不能插队,也不能跳过前面的人。


操作含义:

把一个新元素x放入队列的末尾。

配图理解:

上方是队列原始状态,队头指针h、队尾指针t都指在已有数据上。
当新元素x加入时:

  • 队尾指针t先自增(指向下一个空位);
  • 再把x存进去。

代码实现:

// 入队操作voidpush(intx){q[++t]=x;// 队尾指针先动,再放入新元素}

注意⚠️:
t == N(即队尾指针已经指向数组最后一个位置)时,再插入会导致数组越界(溢出)
因此在真正写程序时,应该先判断是否队满,再执行插入。


3.2 出队(Dequeue):老成员离开队头

想象食堂打饭:排在最前面的人打完饭后离开队伍,其后的人自然往前挪一位。
队列出队的逻辑完全一样。


操作含义:

删除队头的一个元素(最先进入的那一个)。

配图理解:

如图所示:

  • h是队头指针。
  • 当第一个元素出队后,只需将h向后移动一位即可。
  • 队列中的有效数据区随之变化。

代码实现:

// 出队操作voidpop(){h++;// 队头后移,相当于“删除”前面的元素}

注意⚠️:
若队列中已经没有元素,继续出队会导致访问无效数据。
因此在实际应用中,出队前应先判断是否为空队。


3.3 查询队头元素(Front):看看“谁在最前面”

排队的时候,我们有时想知道:现在轮到谁打饭了?

在队列中,我们可以通过一个操作快速“查看”队头,但并不删除它。


操作含义:

返回队列中第一个有效元素。

配图理解:

注意⚠️:
这里返回的不是h指针所指的位置,而是**h + 1** —— 因为h指向的是“队头前一个位置”,真正的队头元素在它的下一个位置。


代码实现:

// 查询队头元素intfront(){returnq[h+1];}

bug提示:
当队列为空时,这样的访问同样会出错(越界或读取垃圾值)。


3.4 查询队尾元素(Back):看看“谁是最后来的”

有时候我们也想知道:刚刚来的那位是谁?
这时就要查看队尾元素


操作含义:

返回队列中最后一个元素(但不删除)。

配图理解:

h是队头,t是队尾。
此时q[t]就是最后一个进入队列的数据。


代码实现:

// 查询队尾元素intback(){returnq[t];}

太棒了💡我已经完全理解你的意图——这两张图片内容属于“第三部分:队列的基本操作——让数据‘有序排队’”,其中展示了队列的两个最核心功能:判空计算元素个数
我会沿用你之前要求的风格结构来重写:生活引入 → 原理解释 → 图解说明 → 代码理解 → 小结提升


3.5 判空:队伍里还有人吗?

想象你在电影院门口排队买票,突然队伍空了——售票员看到前面没人,就知道“该场次的观众都买完票了”。

在队列(Queue)里也一样,我们要经常判断:“这个队列现在是不是空的?


💡 定义理解

在顺序存储的队列中,我们通常用两个指针变量

  • h(head):指向队头,也就是第一个要离开的元素
  • t(tail):指向队尾,也就是最后一个进来的位置

当一个队列完全为空时,说明没有任何人排在里面,那么:

队头指针 h 和 队尾指针 t 会重合。


📊 图解理解

(图示:当 h == t 时,说明队列中没有任何有效元素)


💻 代码逻辑解析

// 判断队列是否为空boolempty(){returnh==t;}

👉 这行代码非常直观:
如果队头和队尾重叠,说明没人排队;否则说明还有人。
一句话总结:

“头尾相遇,队列为空。”


3.6 计算队列中的元素个数:队伍里还有几个人?

当我们知道队列不为空时,接下来的问题自然是:

“那现在队伍里到底还有几个人在等?”


💡 定义理解

在顺序队列中,有效元素的数量可以通过简单的减法得到:

有效元素数 = 队尾指针位置 - 队头指针位置
即:size = t - h


📊 图解理解

(图示:此时有效元素为 b 和 c,因此队列长度为 2)


💻 代码逻辑解析

// 返回队列中有效元素的个数intsize(){returnt-h;}

这个公式就像我们数队伍人数一样:
从第一个人开始数,一直到最后一个站着的人。
如果队头在位置 2,队尾在位置 4,那就有4 - 2 = 2个人。


3.7 测试结果

结语

到这里,咱们用数组模拟队列的核心逻辑就讲透啦 —— 本质就是靠h和t两个指针,用 “左开右闭” 的区间管好队列的入队、出队操作,这种方式在算法竞赛里又快又稳,几乎不会出 bug。
不过实际项目开发里,咱们不用这么 “手动搓轮子”,C++ STL 里自带了现成的queue容器,直接调用接口就能完成队列操作。下一篇博客我就带大家盘一盘 STL 的queue:从基本操作(入队、出队、取队头)到实际场景用法,看完就能直接在项目里用起来~记得蹲我更新哦!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/7 11:50:07

FastExcel技术深度解析:实现Java高效Excel处理的全新路径

FastExcel技术深度解析&#xff1a;实现Java高效Excel处理的全新路径 【免费下载链接】fastexcel Generate and read big Excel files quickly 项目地址: https://gitcode.com/gh_mirrors/fas/fastexcel 还在为Java应用中的Excel处理性能问题而烦恼吗&#xff1f;&#…

作者头像 李华
网站建设 2026/3/29 10:29:11

AI学习之Anthropic的访谈者工具

最近Anthropic出了一篇技术报告&#xff0c;这个报告是Anthropic上线了一个AI访谈工具&#xff0c;然后通过这个访谈工具进行了一系列的访谈&#xff0c;并得到了这些被访谈者对AI的看法&#xff0c;现在让我们来看下这篇文章吧 https://www.anthropic.com/news/anthropic-int…

作者头像 李华
网站建设 2026/3/30 11:53:21

抖音无水印视频下载器:3分钟学会永久保存高清视频

想要收藏抖音上的精彩视频却总是被水印困扰&#xff1f;douyin_downloader抖音无水印下载器正是你需要的完美解决方案。这款开源工具支持抖音视频无水印下载和批量保存&#xff0c;让你轻松收藏喜爱的短视频内容&#xff0c;无论是个人收藏还是内容创作&#xff0c;都能获得原画…

作者头像 李华
网站建设 2026/4/1 17:33:58

如何彻底拯救被撤回的QQ消息:LiteLoaderQQNT防撤回插件完整指南

如何彻底拯救被撤回的QQ消息&#xff1a;LiteLoaderQQNT防撤回插件完整指南 【免费下载链接】LiteLoaderQQNT-Anti-Recall LiteLoaderQQNT 插件 - QQNT 简易防撤回 项目地址: https://gitcode.com/gh_mirrors/li/LiteLoaderQQNT-Anti-Recall 在日常QQ聊天中&#xff0c;…

作者头像 李华