假设有搅乱顺序的一群儿童成一个队列_数据结构与算法系列之栈amp;队列(GO)...

d88ca184ce040a812d739af201c01d92.png

c6a76671eaf138580486dd3a710c1c76.png

以下完整代码均可从这里获取

栈的基本概念

「后进先出、先进后出就是典型的栈结构」。栈可以理解成一种受了限制的线性表,插入和删除都只能从一端进行

当某个数据集合只涉及在一端插入和删除数据,并且满足后进先出、先进后出的特性,就应该首选“栈”这种数据结构(浏览器的前进、后退功能)

栈的实现

栈主要有两种操作,入栈和出栈,这里通过数组(顺序栈)和链表(链式栈)两种方式实现栈

顺序栈

package arrayStackimport "fmt"type Item interface {}type ItemStack struct {Items []ItemN int
}//init stack
func (stack *ItemStack) Init() *ItemStack {stack.Items = []Item{}return stack
}//push stack Item
func (stack *ItemStack) Push(item Item) {if len(stack.Items) > stack.N {fmt.Println("栈已满")return}stack.Items = append(stack.Items, item)
}//pop Item from stack
func (stack *ItemStack) Pop() Item {if len(stack.Items) == 0 {fmt.Println("栈已空")return nil}item := stack.Items[len(stack.Items) - 1]stack.Items = stack.Items[0:len(stack.Items) - 1]return item
}

链式栈

package linkListStackimport "fmt"type Item interface {}type Node struct {Data ItemNext *Node
}type Stack struct {headNode *Node
}//push Stack item
func (stack *Stack) Push(item Item) {newNode := &Node{Data: item}newNode.Next = stack.headNodestack.headNode = newNode
}//pop Item from stack
func (stack *Stack) Pop() Item {if stack.headNode == nil {fmt.Println("栈已空")return nil}item := stack.headNode.Datastack.headNode = stack.headNode.Nextreturn item
}func (stack *Stack) Traverse()  {if stack.headNode == nil {fmt.Println("栈已空")return}currentNode := stack.headNodefor currentNode != nil {fmt.Printf("%vt", currentNode.Data)currentNode = currentNode.Next}
}

栈的应用场景

函数调用栈

❝ 操作系统给每个线程分配了一块独立的内存空间,这块内存被组织成“栈”这种结构, 用来存储函数调用时的临时变量。每进入一个函数,就会将临时变量作为一个栈帧入栈,当被调用函数执行完成,返回之后,将这个函数对应的栈帧出栈
来源:数据结构与算法之美

从这段代码的执行过程中了解函数调用栈

int main() {int a = 1; int ret = 0;int res = 0;ret = add(3, 5);res = a + ret;printf("%d", res);reuturn 0;
}int add(int x, int y) {int sum = 0;sum = x + y;return sum;
}

main()函数调用了 add() 函数,获取计算结果,并且与临时变量 a 相加,最后打印 res 的值。程序在执行过程中,main函数中的变量会先后入栈,当执行到add()函数时,add()函数中的临时变量也会先后入栈,结果如下:

9e2ebe44f53fb115022cc1f580de0329.png

「说明:内存中的堆栈和数据结构堆栈不是一个概念,内存中的堆栈是真实存在的物理区,数据结构中的堆栈是抽象的数据存储结构」

栈在表达式求值中的应用

一个表达式包含两个部分,数字和运算符。我们用两个栈来实现表达式求值,一个栈用来存储数字,一个栈用来存储运算符

假设有这么一个表达式

1000+5*6-6

从左向右遍历表达式,当遇到数字时,将数字放入到存储数字的栈;如果遇到运算符,将存储运算符栈的栈顶元素取出,进行优先级比较

如果比运算符栈顶元素优先级高,则将当前运算符压入到存储运算符的栈中;如果比运算符栈顶元素低或优先级一样,则从存储数字的栈中取出两个元素,然后进行计算,将计算的结果放入到存储数字的栈中。重复上边的操作。过程如图:

fbef2d9d8de010c4e0008df43f0dcb3b.png

栈在括号匹配中的应用

这也是一个比较经典的题,就是给定一个括号串,验证它是否完全匹配,如:

{{}  不匹配
[[{()}]] 匹配
([{}]  不匹配

这个也可以用栈来解决。从左到右遍历括号串,遇到未匹配的左括号则将其压入栈中,遇到右括号时,从栈顶取出一个左括号,如果能匹配,则继续遍历后边的括号,当遍历完之后,栈为空了,说明这个括号串是匹配的,否则是不匹配的。具体实现如下:

package bracketMatchfunc BracketsMatch(str string) bool {brackets := map[rune]rune{')':'(', ']':'[', '}':'['}var stack []runefor _, char := range str {if char == '(' || char == '[' || char == '{' {stack = append(stack, char)} else if len(stack) > 0 && brackets[char] == stack[len(stack) - 1] {stack = stack[:len(stack) - 1]} else {return false}}return len(stack) == 0
}

队列

队列的基本概念

「先进先出就是典型的队列结构」,队列也可以理解成一种受了限制的线性表,插入只能从队列尾部进行,删除只能从队列尾部进行。类比排队取票

队列的基本操作也只有两个,入队和出队。队列的应用确实是十分的广泛,如消息队列、阻塞队列、循环队列等

队列的实现

还是通过两种方式实现队列,通过数组实现顺序队列,通过链表实现链式队列

实现队列需要两个指针,一个指向队列头部,一个指向队列尾部

顺序队列

package arrayQueueimport "fmt"type Item interface {}type Queue struct {Queue []ItemLength int
}func (queue *Queue) Init() {queue.Queue = []Item{}
}func (queue *Queue) Enqueue(data Item) {if len(queue.Queue) > queue.Length {fmt.Println("队列满了")return}queue.Queue = append(queue.Queue, data)
}func (queue *Queue) Dequeue() Item {if len(queue.Queue) == 0 {fmt.Println("队列空了")return nil}item := queue.Queue[0]queue.Queue = queue.Queue[1:]return item
}

链式队列

package linkListQueueimport "fmt"type Item interface {}type Node struct {Data ItemNext *Node
}type Queue struct {headNode *Node
}func (queue *Queue) Enqueue(data Item) {node := &Node{Data: data}if queue.headNode == nil {queue.headNode = node} else {currentNode := queue.headNodefor currentNode.Next != nil {currentNode = currentNode.Next}currentNode.Next = node}
}func (queue *Queue) Dequeue() Item {if queue.headNode == nil {fmt.Println("队列空了")return nil}item := queue.headNode.Dataqueue.headNode = queue.headNode.Nextreturn item
}func (queue *Queue) Traverse() {if queue.headNode == nil {fmt.Println("队列空的")return}currentNode := queue.headNodefor currentNode.Next != nil {fmt.Printf("%vt", currentNode.Data)currentNode = currentNode.Next}fmt.Printf("%vt", currentNode.Data)
}

循环队列

为什么会出现循环队列?

看下边这种情况,我有有一个长度是5的队列,目前队列是满的。假设现在我从队头取出3个元素之后,想再往队列中放入数据,其实是放不进去的,此时就出现一个问题,队列有空闲空间,但是却无法向队列中放入数据了

cace5ee1ab2c4c338bddc4a3580a0983.png

其中一个解决办法就是,「数据搬移」。但是这样的话,每次在出队的时候就等于说删除数组下标为0的元素,而且要将后边所有的数据向前搬移,这样就导致出队的时间复杂度由原来的O(1)变成O(n),这种方法显然是不可取的

f3e3c80aa1bdbc9ac6b9a00fda1a63f3.png

第二个办法就是使用一个「循环队列」,很明显就是一个环,这简直和单向循环链表一模一样。具体什么样,大家应该都十分的清楚,它的难点就在于判空和判满

队列为空时:tail == head
队列为满时:(tail+1)%n == head

哎,找规律问题,不行硬记住就可以了,直接看下边如何实现

循环队列的实现

package loopQueueimport "fmt"type Item interface {}const QueueSize = 5
type LoopQueue struct {Items [QueueSize]ItemHead intTail int
}//init
func (queue *LoopQueue) Init() {queue.Head = 0queue.Tail = 0
}//enqueue
func (queue *LoopQueue) Enqueue(data Item) {if ((queue.Tail + 1) % QueueSize) == queue.Head {fmt.Println("队列满了")}queue.Items[queue.Tail] = dataqueue.Tail = (queue.Tail+1) % QueueSize
}//dequeue
func (queue *LoopQueue) Dequeue() Item {if queue.Head == queue.Tail {fmt.Println("队列空了")return nil}item := queue.Items[queue.Head]queue.Head = (queue.Head + 1) % QueueSizereturn item
}

队列的应用场景

阻塞队列和并发队列

阻塞队列

在实际应用中,队列不会是无限长的,队列一旦有长度限制,就会有满的时候,当队列满了的时候,入队操作就会被阻塞,因为没有数据可取。而当队列为空时,出队是阻塞的,直到队列中有数据可取

没错,平时经常用到的「生产者-消费者模型」就是这样,通过队列可以轻松实现一个生产者-消费者模型

基于阻塞队列,还可以通过调整“生产者”和“消费者”的个数,来提高数据的处理效率

并发队列

对于上边的阻塞队列,在多线程的情况下,就会存在多个线程同时操作队列,这个时候就会存在线程安全问题(如果想了解底层原因,可以看我的这篇文章:进程管理之进程同步)

保证线程安全的队列就称之为并发队列,最简单的方式就是在入队和出队的时候加锁。关于锁,也可以看我这里的系列文章:线程锁系列

a1b09bc0e0c66ace5119929122542a26.png

参考资料:

  • 数据结构与算法之美
  • 从零学习数据结构

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/471986.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

datagridview 动态插入图片_挑战一张照片制作动态PPT背景

在PPT中,要做出好看的页面动画效果,常常需要用很多图片和装饰元素。而如果你手头的素材只有一张照片,如何才能快速做出好看的PPT背景效果呢?本期内容,我们就来一起挑战,使用一张照片,制作PPT动态…

sed搜索某行在行末追加_示范sed指定某行插入 追加和全局替换

有时候会有这样的需求,在指定的行后面或者是前面追加一行,这个时候可以使用sed来完成,具体用法如下a\ 在指定的行后面追加一行b\ 在指定的行前面追加一行使用指定的行号追加内容,在使用行号的过程中,需要注意的问题有以…

LeetCode 1941. 检查是否所有字符出现次数相同

文章目录1. 题目2. 解题1. 题目 给你一个字符串 s ,如果 s 是一个 好 字符串,请你返回 true ,否则请返回 false 。 如果 s 中出现过的 所有 字符的出现次数 相同 ,那么我们称字符串 s 是 好 字符串。 示例 1: 输入&…

java初学者定远期目标_JAVA题,新手求解

展开全部类图设计:类设计:package car;public class Car {private String id;private String name;public void setId(String id) {this.id id;}public void setName(String name) {this.name name;}/*** 获取汽车编e69da5e6ba9062616964757a686964616…

LeetCode 1942. 最小未被占据椅子的编号(set)

文章目录1. 题目2. 解题1. 题目 有 n 个朋友在举办一个派对,这些朋友从 0 到 n - 1 编号。 派对里有 无数 张椅子,编号为 0 到 infinity 。 当一个朋友到达派对时,他会占据 编号最小 且未被占据的椅子。 比方说,当一个朋友到达时…

vue路由懒加载_优化vue项目的首屏加载速度

最近使用vue-cli3构建了一个小型的博客系统,完工之后,build打包出来发现一个chunk-vendors包就有1.1m,部署上去之后,访问的时候,首屏加载非常慢。居然需要21s,体验极差。这是打包的结果截图根据这种情况&am…

LeetCode 1943. 描述绘画结果(差分思想)

文章目录1. 题目2. 解题1. 题目 给你一个细长的画,用数轴表示。 这幅画由若干有重叠的线段表示,每个线段有 独一无二 的颜色。 给你二维整数数组 segments ,其中 segments[i] [starti, endi, colori] 表示线段为 半开区间 [starti, endi) 且…

LeetCode 1944. 队列中可以看到的人数(单调栈)

文章目录1. 题目2. 解题1. 题目 有 n 个人排成一个队列,从左到右 编号为 0 到 n - 1 。 给你以一个整数数组 heights ,每个整数 互不相同,heights[i] 表示第 i 个人的高度。 一个人能 看到 他右边另一个人的条件是这两人之间的所有人都比他…

yolov2训练_一文看懂YOLO v2

我的CSDN博客:https://blog.csdn.net/litt1e我的公众号:工科宅生活概述新的YOLO版本论文全名叫“YOLO9000: Better, Faster, Stronger”,相较于YOLO主要有两个大方面的改进:第一,作者使用了一系列的方法对原来的YOLO多…

java的foeachr循环_for循环和Dowhile循环的应用

DoWhile循环{public static void main(String[] args) {int i 0;int sum 0;do {sum sum i;i;} while (i < 100);System.out.println(sum);}}{int a 0;while (a<0){System.out.println(a);a;}System.out.println("");do {System.out.println(a);a;}while (…

LeetCode 1945. 字符串转化后的各位数字之和

文章目录1. 题目2. 解题1. 题目 给你一个由小写字母组成的字符串 s &#xff0c;以及一个整数 k 。 首先&#xff0c;用字母在字母表中的位置替换该字母&#xff0c;将 s 转化 为一个整数&#xff08;也就是&#xff0c;‘a’ 用 1 替换&#xff0c;‘b’ 用 2 替换&#xff…

LeetCode 1955. 统计特殊子序列的数目

文章目录1. 题目2. 解题1. 题目 特殊序列 是由 正整数 个 0 &#xff0c;紧接着 正整数 个 1 &#xff0c;最后 正整数 个 2 组成的序列。 比方说&#xff0c;[0,1,2] 和 [0,0,1,1,1,2] 是特殊序列。 相反&#xff0c;[2,1,0] &#xff0c;[1] 和 [0,1,2,0] 就不是特殊序列。…

win7家庭版远程桌面补丁_无需惊慌!微软漏洞数月后再被“预警”打补丁即可防御...

9月7日&#xff0c;火绒接到用户反馈&#xff0c;咨询多家安全友商相继发布的漏洞预警一事。经火绒工程师确认&#xff0c;该漏洞(CVE-2019-0708)早在5月14日就已经被微软披露过&#xff0c;火绒也在第一时间发布了相关漏洞预警&#xff0c;并向火绒用户推送该漏洞补丁。此次多…

LeetCode 1954. 收集足够苹果的最小花园周长(数学)

文章目录1. 题目2. 解题1. 题目 给你一个用无限二维网格表示的花园&#xff0c;每一个 整数坐标处都有一棵苹果树。 整数坐标 (i, j) 处的苹果树有 ∣i∣∣j∣|i| |j|∣i∣∣j∣个苹果。 你将会买下正中心坐标是 (0, 0) 的一块 正方形土地 &#xff0c;且每条边都与两条坐标…

java星座测试需求分析_java十二星座 (快来测试你是什么星座吧)

无聊写个十二星座。。我发现我好像都是无聊的时候学习的。。。package 十二星座;import java.awt.*;import javax.swing.*;import java.awt.event.*;public class 十二星座 {public static void main(String args[]){new Fram();}}class Fram extends JFrame implements Action…

LeetCode 1953. 你可以工作的最大周数

文章目录1. 题目2. 解题1. 题目 给你 n 个项目&#xff0c;编号从 0 到 n - 1 。 同时给你一个整数数组 milestones &#xff0c;其中每个 milestones[i] 表示第 i 个项目中的阶段任务数量。 你可以按下面两个规则参与项目中的工作&#xff1a; 每周&#xff0c;你将会完成 …

vim查找关键字_VIM学习笔记 对话框(Dialog)

查找对话框使用以下命令&#xff0c;将会打开一个查找对话框&#xff1a;:promptfind [string]如果在命令中指定了[string]值&#xff0c;那么就会查找该字符串&#xff1b;如果没有指定[string]值&#xff0c;那么将会查找上次搜索的字符串。替换对话框使用以下命令&#xff0…

LeetCode 1952. 三除数

文章目录1. 题目2. 解题1. 题目 给你一个整数 n 。如果 n 恰好有三个正除数 &#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 如果存在整数 k &#xff0c;满足 n k * m &#xff0c;那么整数 m 就是 n 的一个 除数 。 示例 1&#xff1a; 输入&#xff…

Java充电宝模型设计_继续探讨点赞功能模块设计

继续探讨点赞功能模块设计前几天我们设计了点赞模块的模块&#xff0c;大致思路就是&#xff1a;用户点赞&#xff0c;首先缓存到redis中进行保存&#xff0c;redis中既要保存点赞总数&#xff0c;还要保存点赞记录。然后定时执行redis数据到数据库中。但是&#xff0c;今天在资…

LeetCode 1957. 删除字符使字符串变好

文章目录1. 题目2. 解题1. 题目 一个字符串如果没有 三个连续 相同字符&#xff0c;那么它就是一个 好字符串 。 给你一个字符串 s &#xff0c;请你从 s 删除 最少 的字符&#xff0c;使它变成一个 好字符串 。 请你返回删除后的字符串。题目数据保证答案总是 唯一的 。 示…