当前位置: 首页 > news >正文

0基础入门Go线性结构:栈和队列

文章目录

  • 0基础入门Go线性结构:栈和队列
    • 一、栈(Stack):像叠衣服一样,“后进先出”
      • 1\. 栈的核心操作(0基础也能懂)
      • 2\. Go实现极简栈(无复杂语法,复制就能跑)
      • 3\. 栈的实际用途(知道用在哪,才好理解)
    • 二、队列(Queue):像排队买奶茶一样,“先进先出”
      • 1\. 队列的核心操作(和栈对比着记,更简单)
      • 2\. Go实现极简线性队列(同样无复杂语法)
      • 3\. 队列的实际用途(贴近生活,好记)
    • 三、栈和队列的核心区别(一张表看懂,0基础不混淆)
    • 四、0基础总结(重点记这3点,不迷路)

0基础入门Go线性结构:栈和队列

【引言】

在上一篇博客中,我们详细讲解了Go中切片的核心操作——copy方法,了解了切片作为顺序表的基础特性,而今天我们要在此基础上,延伸学习线性结构中最常用的两个“工具”——栈和队列。它们既是切片应用的重要场景,也是后续我们学习哈希表(下一篇内容)的基础,相当于“承上启下”的关键知识点:切片的特性决定了栈和队列的常见实现方式,而栈和队列的线性逻辑,又能帮助我们更好地理解后续哈希表的非线性存储逻辑,跟着节奏一步步来,0基础也能轻松吃透。

大家好,我是刚入门Go的编程小白,今天想和大家分享两个最基础、最常用的线性结构——栈和队列。很多0基础的朋友一听到“数据结构”就头大,觉得是高深莫测的东西,但其实它们就藏在我们的日常生活里,用类比的方式一讲就懂。

先跟大家说个小前提:什么是「线性结构」?简单说就是“数据像排队一样,排成一条直线,每个数据只有一个前一个和一个后一个(除了开头和结尾)”,就像我们排队买奶茶、叠衣服,都是线性的,没有分支。栈和队列,就是线性结构里最典型的两个“小伙伴”,只是它们的“使用规则”不一样。

一、栈(Stack):像叠衣服一样,“后进先出”

先想一个生活场景:我们叠衣服的时候,总是把先洗好的衣服放在最下面,后洗好的衣服叠在上面。等要穿的时候,只能从最上面开始拿,先拿最后叠的那件,才能拿到下面的。这就是栈的核心逻辑——后进先出(LIFO),也可以叫“先进后出”。

用更直白的话讲:栈就像一个“密封的圆筒”,只有一个开口,数据只能从这个开口“放进去”(入栈),也只能从这个开口“拿出来”(出栈),不能从中间插着放、也不能从中间抽出来。

为了更直观理解,我们用一个简单的示意图展示栈的结构和核心操作(类比叠衣服),示意图放在代码块里,清晰醒目:

【栈的文字示意图(类比叠衣服)】 ┌─────────┐ │ 3 │ ← 栈顶(最后入栈,最先出栈,类比最上面的衣服) │ 2 │ ← 中间元素 │ 1 │ ← 栈底(最先入栈,最后出栈,类比最下面的衣服) └─────────┘ 操作流程说明: 1. 入栈:1 → 2 → 3(依次叠在上面,和叠衣服一致,先叠1,再叠2,最后叠3); 2. 出栈:3 → 2 → 1(先拿最上面的3,再拿2,最后拿最下面的1); 3. 全程只有栈顶一个操作口,符合“后进先出”逻辑。

1. 栈的核心操作(0基础也能懂)

  • 入栈(Push):把数据“叠”到栈的最上面(就像叠衣服时,新衣服放在最顶层);

  • 出栈(Pop):把栈最上面的数据“拿”走(就像拿衣服时,先拿最顶层的);

  • 查看栈顶(Peek):看看最上面是什么数据,但不拿走(就像看看叠在最上面的是哪件衣服);

  • 判断空栈(IsEmpty):看看栈里有没有数据(就像看看衣服叠完了没,或者拿完了没)。

2. Go实现极简栈(无复杂语法,复制就能跑)

Go里没有自带的栈结构,但我们用「切片」就能轻松实现一个,切片的append(添加)和len(长度)方法,刚好对应栈的入栈和判断空栈,非常简单。

补充:栈的实现有两种常见方式——顺序表(如切片、数组)和链表,两者各有优劣,并非顺序表绝对最优,需结合数据量和场景选择:

1. 顺序表(切片/数组):日常开发中小数据量场景最常见。栈的核心操作是“末尾入栈、末尾出栈”,顺序表的末尾操作(append、删除末尾元素)时间复杂度是O(1),效率极高,且代码简单;但需注意扩容和内存浪费问题:切片扩容时会申请更大的内存空间(通常是原容量的2倍),若数据量极大后又大幅减少,扩容后的空闲内存无法释放,会造成内存浪费,且大数据量下频繁扩容也会有性能损耗。

2. 链表:大数据量、需频繁增减数据的场景更优。链表实现栈无需担心扩容问题,增减元素时只需修改指针,无需移动大量数据,也不会造成内存浪费;且链表的内存是按需分配,数据减少时内存可及时释放,适合大数据处理场景。但链表需要额外维护指针,代码比顺序表复杂,日常小数据量场景下几乎不用。

栈的两种实现方式对比示意图(放在代码块里,直观区分):

【栈的两种实现方式对比】 ┌─────────────────────────────────────────────────┐ │ 顺序表(切片)实现栈 │ ├─────────────────────────────────────────────────┤ │ 初始化:空切片(var stack []int) │ │ 入栈:append(stack, num) (末尾添加,O(1)) │ │ 出栈:stack = stack[:len(stack)-1](O(1)) │ │ 优点:代码简单、操作高效 │ │ 缺点:大数据量扩容后易造成内存浪费 │ └─────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────┐ │ 链表实现栈 │ ├─────────────────────────────────────────────────┤ │ 初始化:定义链表节点,维护头指针(栈顶) │ │ 入栈:修改头指针,添加新节点(O(1)) │ │ 出栈:修改头指针,删除栈顶节点(O(1)) │ │ 优点:无扩容问题、不浪费内存 │ │ 缺点:代码复杂、需维护指针 │ └─────────────────────────────────────────────────┘
packagemainimport"fmt"// 定义栈结构,用切片存储数据(切片本身就是线性的,完美适配)typeStackstruct{data[]int// 存储栈里的数据,这里用int举例,其他类型也可以}// 入栈:往栈顶添加数据(类比叠衣服)func(s*Stack)Push(numint){s.data=append(s.data,num)// 切片末尾添加,就是栈顶}// 出栈:从栈顶删除并返回数据(类比拿衣服)func(s*Stack)Pop()(int,error){// 先判断栈是否为空,空栈不能出栈ifs.IsEmpty(){return0,fmt.Errorf("栈为空,无法出栈")}// 切片最后一个元素就是栈顶,先取出,再删除top:=s.data[len(s.data)-1]s.data=s.data[:len(s.data)-1]returntop,nil}// 查看栈顶:只看不删func(s*Stack)Peek()(int,error){ifs.IsEmpty(){return0,fmt.Errorf("栈为空,无栈顶元素")}returns.data[len(s.data)-1],nil}// 判断栈是否为空func(s*Stack)IsEmpty()bool{returnlen(s.data)==0}// 测试一下,看看效果funcmain(){// 初始化一个空栈stack:=&Stack{}// 入栈:1->2->3(类比叠衣服:1在最下,3在最上)stack.Push(1)stack.Push(2)stack.Push(3)fmt.Println("入栈后,栈顶元素:",stack.Peek())// 输出3// 出栈:先出3,再出2(类比拿衣服:先拿最上面的3)pop1,_:=stack.Pop()fmt.Println("第一次出栈:",pop1)// 输出3pop2,_:=stack.Pop()fmt.Println("第二次出栈:",pop2)// 输出2fmt.Println("出栈两次后,栈是否为空?",stack.IsEmpty())// 输出false(还有1)}

3. 栈的实际用途(知道用在哪,才好理解)

不用记复杂的场景,记住两个最常见的:

  • 浏览器的“后退”功能:你浏览页面1→页面2→页面3,后退时先退页面3,再退页面2,就是栈的逻辑;

  • 括号匹配:比如判断 (())、()() 是否合法,就是用栈来存储左括号,遇到右括号就出栈匹配,0基础暂时不用深究,知道就行。

二、队列(Queue):像排队买奶茶一样,“先进先出”

讲完栈,再看队列,它的主流逻辑和栈刚好相反,但要注意:队列并非只有一种逻辑,也存在先进后出的特殊队列(如双端队列的特殊使用场景),不过我们今天重点讲「线性结构的常规队列」。还是用生活场景类比常规队列:排队买奶茶,先到的人排在前面,先拿到奶茶;后到的人排在后面,只能等前面的人都拿到了,才能轮到自己。这就是线性结构常规队列的核心逻辑——先进先出(FIFO)

队列就像一个“两头开口的管道”,数据只能从“一端进”(队尾),从“另一端出”(队头),不能插队、也不能从中间进出,完美贴合线性结构的特点。

用生活场景类比+文字示意图(放在代码块里),理解队列的核心操作:

【队列的文字示意图(类比排队买奶茶)】 ┌─────────┬─────────┬─────────┐ │ 1(队头) │ 2(中间) │ 3(队尾) │ ← 排队的顾客(数据) └─────────┴─────────┴─────────┘ ↓ ↓ ↓ 出队口 中间位置 入队口 操作流程说明: 1. 入队:4 → 排在3后面(队尾),队列变成 [1,2,3,4]; 2. 出队:1先离开(队头),队列变成 [2,3,4]; 3. 全程只能从队尾入、队头出,符合“先进先出”逻辑。

示意图说明:排队买奶茶时,顾客依次排到队尾(入队),先到的顾客先买完离开(出队),和队列“先进先出”的逻辑完全一致。

补充说明:队列有很多类型(比如循环队列、链式队列、双端队列等),其中双端队列可实现先进后出的效果(类似栈),但我们今天只聚焦「线性结构的常规队列」(也就是顺序队列),其核心逻辑是先进先出,不用管其他复杂类型和特殊场景,0基础先掌握这个核心就够了。

1. 队列的核心操作(和栈对比着记,更简单)

  • 入队(Enqueue):把数据放到队列的末尾(就像排队时,站到队尾);

  • 出队(Dequeue):把队列最前面的数据拿走(就像排队时,最前面的人买完奶茶离开);

  • 查看队头(Front):看看最前面是什么数据,但不拿走(就像看看队伍最前面的人是谁);

  • 判断空队列(IsEmpty):看看队列里有没有数据(就像看看队伍有没有人)。

2. Go实现极简线性队列(同样无复杂语法)

和栈一样,Go也没有自带的队列,我们还是用切片实现,重点区分“队头”和“队尾”——切片的开头是队头,切片的末尾是队尾,入队就是append,出队就是删除切片的第一个元素。

补充:线性队列的实现同样有顺序表(切片、数组)和链表两种方式,两者各有适用场景,日常开发中顺序表(切片)更常见,但需注意其局限性:顺序表实现队列时,出队(删除第一个元素)需要移动所有元素,时间复杂度是O(n)(元素越多,移动越慢)。如果队列数据量小、操作不频繁,顺序表(切片)足够用;若数据量大、出队频繁,链表实现更优——链表出队时只需修改头指针,时间复杂度O(1),无需移动元素,效率更高(但链表代码稍复杂,0基础可先掌握顺序表实现)。

队列两种实现方式对比示意图(放在代码块里,清晰易读):

【队列的两种实现方式对比】 ┌─────────────────────────────────────────────────┐ │ 顺序表(切片)实现队列 │ ├─────────────────────────────────────────────────┤ │ 初始化:空切片(var queue []int) │ │ 入队:append(queue, num) (队尾,O(1)) │ │ 出队:queue = queue[1:] (移动元素,O(n)) │ │ 优点:代码简单、小数据量适用 │ │ 缺点:大数据量出队慢、可能浪费内存 │ └─────────────────────────────────────────────────┘ ┌─────────────────────────────────────────────────┐ │ 链表实现队列 │ ├─────────────────────────────────────────────────┤ │ 初始化:定义链表节点,维护头、尾指针 │ │ 入队:修改尾指针,添加新节点(O(1)) │ │ 出队:修改头指针,删除队头节点(O(1)) │ │ 优点:大数据量高效、无扩容浪费 │ │ 缺点:代码复杂、需维护指针 │ └─────────────────────────────────────────────────┘
packagemainimport"fmt"// 定义队列结构,用切片存储数据(线性结构)typeQueuestruct{data[]int// 存储队列数据,int类型举例}// 入队:往队尾添加数据(类比排队站到队尾)func(q*Queue)Enqueue(numint){q.data=append(q.data,num)// 切片末尾添加,就是队尾}// 出队:从队头删除并返回数据(类比排队最前面的人离开)func(q*Queue)Dequeue()(int,error){// 先判断队列是否为空,空队列不能出队ifq.IsEmpty(){return0,fmt.Errorf("队列为空,无法出队")}// 切片第一个元素是队头,先取出,再删除front:=q.data[0]q.data=q.data[1:]// 切片从索引1开始,相当于删除第一个元素returnfront,nil}// 查看队头:只看不删func(q*Queue)Front()(int,error){ifq.IsEmpty(){return0,fmt.Errorf("队列为空,无队头元素")}returnq.data[0],nil}// 判断队列是否为空func(q*Queue)IsEmpty()bool{returnlen(q.data)==0}// 测试一下funcmain(){// 初始化一个空队列queue:=&Queue{}// 入队:1->2->3(类比排队:1在最前,3在队尾)queue.Enqueue(1)queue.Enqueue(2)queue.Enqueue(3)fmt.Println("入队后,队头元素:",queue.Front())// 输出1// 出队:先出1,再出2(类比排队:1先买完离开,然后是2)deq1,_:=queue.Dequeue()fmt.Println("第一次出队:",deq1)// 输出1deq2,_:=queue.Dequeue()fmt.Println("第二次出队:",deq2)// 输出2fmt.Println("出队两次后,队列是否为空?",queue.IsEmpty())// 输出false(还有3)}

3. 队列的实际用途(贴近生活,好记)

  • 消息通知:我们手机收到的消息,按时间顺序排列,先收到的先显示、先处理,就是队列;

  • 打印队列:多个人同时打印文件,打印机按顺序打印,先提交的先打印,后提交的排队,也是队列的逻辑。

三、栈和队列的核心区别(一张表看懂,0基础不混淆)

对比项栈(Stack)线性队列(Queue)
核心逻辑后进先出(LIFO),像叠衣服先进先出(FIFO),像排队买奶茶
操作端只有一个开口(栈顶),入栈、出栈都在这两个开口(队头出、队尾进)
Go实现切片append(入栈)、删除末尾(出栈)切片append(入队)、删除开头(出队)
最优/常见实现小数据量:顺序表(切片);大数据量:链表小数据量:顺序表(切片);大数据量:链表
生活类比叠衣服、浏览器后退、装快递箱子排队买奶茶、打印文件、消息通知

补充:表格中两种结构的实现选择,可结合上面的示意图理解,小数据量优先选顺序表(切片)(简单高效),大数据量优先选链表(无扩容、不浪费内存)。

四、0基础总结(重点记这3点,不迷路)

  1. 栈和队列都是「线性结构」,数据排成一条直线,没有分支;

  2. 栈:后进先出(先放的后拿),只有一个操作口,Go用切片就能实现;实现上,小数据量用顺序表(切片)最常见,大数据量、需频繁增减数据时,链表更优(无扩容烦恼、不浪费内存),纠正“顺序表绝对最优”的误区,兼顾扩容和内存浪费问题。

  3. 线性队列:主流逻辑是先进先出(先放的先拿),有两个操作口(队头出、队尾进),同样用切片实现;也存在先进后出的特殊线性队列(如双端队列的特殊使用),但常规场景下以先进先出为主;实现上,小数据量用顺序表(切片)最常见,大数据量用链表更优。

其实数据结构没有那么可怕,只要把抽象的逻辑和生活中的场景结合起来,再动手跑一跑简单的代码,0基础也能轻松掌握。今天的栈和队列就讲到这里,后续可以再深入学习更复杂的队列类型,但先把这个基础打牢就够啦~

如果大家有不懂的地方,或者想补充其他知识点,欢迎在评论区留言哦!

【结语】

到这里,Go中栈和队列的核心知识点就全部讲解完毕了。我们不难发现,栈和队列的实现始终围绕着上一篇提到的切片(顺序表)展开,同时也了解了链表在大数据场景下的优势,这既是对切片copy知识点的延伸,也为我们下一篇要学习的哈希表做好了铺垫。下一篇,我们将跳出线性结构的范畴,学习非线性结构中的哈希表,看看它如何解决栈和队列在查找效率上的局限,继续跟着节奏,一步步夯实Go数据结构的基础~

关注我,点赞👍、收藏⭐本篇内容

(注:文档部分内容可能由 AI 生成)

http://www.jsqmd.com/news/775577/

相关文章:

  • 吊车证报考机构排行:正规资质与实操实力对比 - 奔跑123
  • 2026年口碑好的餐饮全案设计公司推荐,专业服务公司 - mypinpai
  • 移动端协同应用开发实战:基于React Native与CRDT的架构设计与优化
  • MOS管开关注意事项尖峰吸收保护分析
  • 如何快速将永辉超市购物卡变现?这里有三个实用方法! - 团团收购物卡回收
  • 【读书笔记】《伊朗简史》
  • 2026广东美妆代工实测封神!5款广州等地冻干粉源头OEM厂家直销实力靠谱口碑佳 - 十大品牌榜
  • .NET 接口限流、防重、幂等性设计
  • com0com实战指南:Windows虚拟串口深度解析与效率提升
  • 5分钟完成Degrees of Lewdity游戏美化:DoL-Lyra整合包完整指南
  • 3大痛点解析:如何深度优化AMD处理器性能并实现游戏帧率稳定提升
  • 2026最新防火卷帘门/防火门/防火窗/单元门/钢质门企业推荐!辽宁优质权威榜单发布,沈阳靠谱企业实力入围 - 十大品牌榜
  • 内容创作团队如何利用多模型能力批量生成与优化文案
  • 武汉室内设计公司靠谱吗?UWD有无设计告诉你 - mypinpai
  • 如何快速解决中文文献管理难题:终极茉莉花插件使用指南
  • 开源AI技能库:标准化与复用,提升智能体开发效率
  • 广州西服定制推荐,精选进口面料,每一寸都是高级感 - 十大品牌榜
  • 如何快速掌握wxappUnpacker:微信小程序逆向工程的完整实战指南
  • 大润发购物卡回收流程详解,新手小白也能秒懂! - 团团收购物卡回收
  • 2026年北京井木装饰在服装行业的排名,有名的装修公司推荐 - mypinpai
  • Windows右键菜单如何告别杂乱?这款专业管理工具给你终极解决方案
  • B-52轰炸机内部,没有MCU的时代,一台纯机械设备,竟能计算天空坐标
  • Company Registered Address 2026.05.06
  • Adobe Acrobat Pro 2025下载安装使用教程
  • 永辉超市购物卡换现金,这些平台帮你高价回收 - 团团收购物卡回收
  • AI代理协作自动化生成n8n工作流:从需求到生产部署全流程
  • 智能防抖解决方案:KeyboardChatterBlocker在机械键盘输入优化领域的应用
  • KiCad 3D模型库不够用?试试这个骚操作:把立创EDA的封装变成你的私人模型库
  • InnoDB 中索引类型有哪些?
  • 2026年论文降低AI率收藏指南:学姐实测AIGC免费降重,盘点5款实用降AI率工具 - 降AI实验室