算法day9

算法day9

  • 栈与队列基础
  • 232用栈实现队列
  • 225用队列实现栈

栈与队列理论基础

言简意赅:栈的原理就是后进先出。队列就是先进先出。请添加图片描述
相关操作:
栈:入栈,出栈,判栈空,取栈顶元素。
队列:出队,入队,判队空等。
这些操作都可以用数组来模拟。
golang和c++不一样,这些类型都需要自己去自定义数据类型模拟来实现。
c++我当时很多时候都习惯用现成的。

下面是一些简单的模拟:
golang实现栈和栈的操作

package mainimport "fmt"type Stack []int//入栈操作
func (s *Stack) Push(value int) {*s = append(*s, value)
}//出栈操作,返回值是删除的这个元素的值和是否删除成功。
func (s *Stack) Pop() (int, bool) {if len(*s) == 0 {return 0, false}index := len(*s) - 1 // 先计算栈顶元素的下标索引。element := (*s)[index] //然后取出这个元素*s = (*s)[:index] // 然后就不要后面这个元素了。return element, true //返回值
}// 判栈空就是判数组长度是否为0。
func (s *Stack) IsEmpty() bool {return len(*s) == 0
}func main() {var stack Stack //初始化stack.Push(1) //调用push函数stack.Push(2)stack.Push(3)fmt.Println(stack) // Output: [1 2 3]if value, ok := stack.Pop(); ok {//出栈fmt.Println("Popped:", value) // Output: Popped: 3}fmt.Println(stack) // Output: [1 2] 输出
}

总结就是控制数组的一端变化就完事了。

golang实现队列和队列的相关操作
还是数组模拟

package mainimport "fmt"type Queue []int// 入队操作,直接操作切片append目标元素就完事了,因为在队尾插入。
func (q *Queue) Enqueue(value int) {*q = append(*q, value)
}// 删除对头元素
func (q *Queue) Dequeue() (int, bool) {if len(*q) == 0 {return 0, false}element := (*q)[0]  // Get the first element. //直接访问队头元素就是0号*q = (*q)[1:]       // 进行切片操作,最前面的元素不要了return element, true
}// 判空
func (q *Queue) IsEmpty() bool { return len(*q) == 0
}func main() {var q Queueq.Enqueue(1)q.Enqueue(2)q.Enqueue(3)fmt.Println(q) // Output: [1 2 3]if value, ok := q.Dequeue(); ok {fmt.Println("Dequeued:", value) // Output: Dequeued: 1}fmt.Println(q) // Output: [2 3]
}

总结:
个人感觉golang模拟比c++用数组模拟好多了。因为切片操作真的很方便。


用栈实现队列
就是使用栈实现队列的下列操作:
push(x):将一个元素放入队列的尾部
pop():从队列首部移除元素
peek():返回队列首部的元素
empty():返回队列是否为空

题目也说了,栈里面的pop,push,size,isempty是合法操作。

核心:
首先这个过程要想清楚:就是用栈是怎么去实现队列的操作的。请添加图片描述
实现的方法是用双栈,我想出栈和队列得到统一,那我就要用到下面这个outstack来模拟。进栈操作就是用instack来模拟。
但是有一些细节需要去注意:这个过程结合模拟来比较好,在push数据的时候,只要数据放进输入栈就好,但在pop的时候,操作就复杂一些,输出栈如果为空,就把进栈数据全部导入进来(注意是全部导入),再从出栈弹出数据,如果输出栈不为空,则直接从出栈弹出数据就可以了。

最后如何判断队列为空?如果进栈和出栈都为空的话,就说明模拟的队列为空了。

技巧,实现一些功能的时候其实不用再写一次逻辑,可以直接调函数。

ps:里面的错误处理是返回-1

type MyQueue struct {stackIn []intstackout []int
}func Constructor() MyQueue {return MyQueue{stackIn:make([]int,0),stackout:make([]int,0),}
}func (this *MyQueue) Push(x int)  {this.stackIn = append(this.stackIn, x)
}func (this *MyQueue) Pop() int {inlen,outlen:=len(this.stackIn),len(this.stackout)if outlen == 0{if inlen==0{return -1}for i:=inlen-1;i>=0;i--{this.stackout=append(this.stackout, this.stackIn[i])}this.stackIn=[]int{}outlen = len(this.stackout)}val:=this.stackout[outlen-1]this.stackout=this.stackout[:outlen-1]return val
}func (this *MyQueue) Peek() int {val:= this.Pop()if val == -1{return -1}this.stackout=append(this.stackout, val)return val
}func (this *MyQueue) Empty() bool {return len(this.stackIn)==0 && len(this.stackout)==0
}/*** Your MyQueue object will be instantiated and called as such:* obj := Constructor();* obj.Push(x);* param_2 := obj.Pop();* param_3 := obj.Peek();* param_4 := obj.Empty();*/

每个函数实现的思路解读请添加图片描述
我还是把这个图放在这里:
结构体:

type MyQueue struct {stackIn  []int //输入栈stackOut []int //输出栈
}

为什么这么写,这里就是代表两个栈代表了这个队列。

构造函数,就是把这个队列的组成,两个模拟栈初始化。
func Constructor() MyQueue {
return MyQueue{
stackIn:make([]int,0),
stackout:make([]int,0),
}
}

队列push操作,就是直接往入栈里面加元素,就完成了入队操作
func (this *MyQueue) Push(x int) {
this.stackIn = append(this.stackIn, x)
}

出队操作
func (this *MyQueue) Pop() int {
inLen, outLen := len(this.stackIn), len(this.stackOut)
if outLen == 0 {
if inLen == 0 {
return -1
}
for i := inLen - 1; i >= 0; i-- {
this.stackOut = append(this.stackOut, this.stackIn[i])
}
this.stackIn = []int{} //导出后清空
outLen = len(this.stackOut) //更新长度值
}
val := this.stackOut[outLen-1]
this.stackOut = this.stackOut[:outLen-1]
return val
}
这个稍微难了一点点,这里的关键是要把逻辑想清楚。这个要靠自己模拟得出来为什么这么写。
出队:出栈不为空,就直接进行出栈,如果出栈为空,那就把入栈元素全部进入出栈,然后再进行出栈。(这个过程要自己去模拟看看为什么),你可以去看看你不这么做会发生什么结果(顺序会混乱)。
然后正确的逻辑我建议按照上面这个图想了写。
思考:
因为你模拟队列,入第一个栈的时候你这个时候进行弹栈输出你得到的序列肯定是反的,所以再来一个栈,去接弹栈的结果,此时第二个栈出栈的效果就很你出队的效果是一样的,顺序都是正的,所以对这第二个栈出栈就达到了队列出队的目的。

获取对头的元素值
func (this *MyQueue) Peek() int {
val:= this.Pop()
if val == -1{
return -1
}
this.stackout=append(this.stackout, val)
return val
}
这里是前面实现的方法的复用,我可以先使用队列的出队操作,将对头出队获得这个元素,然后我再对这个出栈再把这个出队的元素进行入栈即可。

判空操作就是,两个栈都空,那就代表没有元素,队列为空。
func (this *MyQueue) Empty() bool {
return len(this.stackIn)==0 && len(this.stackout)==0
}


225用队列实现栈

使用队列来完成栈的操作
由于go语言没有像c++一样的stack和queue,所以题目中队列的那些合法操作可以说鸟用没有。都需要自己来模拟,所以自己有那个思想,自己来模拟每一个需要的操作就行了。而且go语言的切片操作非常的强大,完成这些操作我觉得都是一步到位的。

本题主要用队列实现栈的一下操作:
push(x) – 元素 x 入栈
pop() – 移除栈顶元素
top() – 获取栈顶元素
empty() – 返回栈是否为空

解法:用一个队列就可以,我个人认为两个队列反而还难理解了。

两个队列的版本:
核心思想就是往栈的性质去靠:
一个队列为主队列,一个为辅助队列,当入栈操作时,我们先将主队列内容导入辅助队列,然后将入栈元素放入主队列队头位置,再将辅助队列内容,依次添加进主队列即可。
这里我不写这种,这种我个人认为是十分的麻烦,理解起来也不好理解。

一个队列的版本:
使用一个队列时,为了满足栈的特性,即最后入栈的元素最先出栈,同样需要满足队列前端的元素是最后入栈的元素。

入栈操作时,首先获得入栈前的元素的个数,然后将元素入队到队列,再将队列中的前n个元素(即除了心如站的元素之外的全部元素)依次出队并入队到队列,此时队列的前端的元素即为新入栈的元素,且队列的前端和后端分别对应栈顶和栈底。

由于每次入栈操作都确保队列的前端元素为栈顶元素,因此出栈操作和获得栈顶元素操作都可以简单实现。出栈操作只需要移除队列的前端元素并返回即可。获得栈顶元素操作只需要获得队列的前端元素并返回即可(不移除元素)。

我总结一下:
就是入栈的时候我只管先加进来,然后前面的元素依次出队然后往后面依次入队,这样保证了我加进来的这个元素一定是对头元素,这就满足了后进先出,此时我队列出队时出的这个元素就达到了这种效果。
举个例子
栈 1-2-3-4-5 ,123先入栈
队列入1, 1
入2, 2-1
入3-2-1,
出3,3直接删除 现在队列2-1
4入队 4-2-1
5入队5-4-2-1,显然可以发现,这简直和栈完全是一个状态,我删队头就是弹栈顶。
这样这个题大致的思路已经理清楚了

type MyStack struct {queue []int
}func Constructor() MyStack {return MyStack{queue:make([]int,0),}
}func (this *MyStack) Push(x int)  {n:=len(this.queue)this.queue=append(this.queue,x)for ;n>0;n--{this.queue=append(this.queue,this.queue[0])this.queue=this.queue[1:]}
}func (this *MyStack) Pop() int {val := this.queue[0]this.queue=this.queue[1:]return val
}func (this *MyStack) Top() int {return this.queue[0]
}func (this *MyStack) Empty() bool {return len(this.queue)==0
}/*** Your MyStack object will be instantiated and called as such:* obj := Constructor();* obj.Push(x);* param_2 := obj.Pop();* param_3 := obj.Top();* param_4 := obj.Empty();*/

我再来解释一下代码逻辑

type MyStack struct {queue []int
}

现在我的队列就是我要拿来模拟栈,所以此时我的栈就是队列。


func Constructor() MyStack {return MyStack{queue:make([]int,0),}
}

这就是个初始化函数。

入栈操作:

func (this *MyStack) Push(x int)  {n:=len(this.queue)this.queue=append(this.queue,x)for ;n>0;n--{this.queue=append(this.queue,this.queue[0])this.queue=this.queue[1:]}
}

为什么这样的操作能行我在上面已经做了相关的模拟:
1.在入栈之前我先进行队列元素统计,因为我统计的这些元素都要进行依次的后移操作。
2.然后把这个元素加进来。
3.开始把这个元素前面的元素依次后移,这个就是把队头元素依次的append到队尾,这就达到了队头元素后移的效果,然后再用个切片操作把队头不要了,这就达到了队头出队的效果。

出队操作

func (this *MyStack) Pop() int {val := this.queue[0]this.queue=this.queue[1:]return val
}

前面我已经证明过了,出队操作就是直接出,因为我push函数操作的原因,此时队头就是栈顶,出队和出栈一个效果。

获取栈顶元素
func (this *MyStack) Top() int {
return this.queue[0]
}
那更是简单,就是访问队头.

判空,现在队列和这个栈可以说就是一回事,因为底层就一个切片结构。所以直接用长度判

func (this *MyStack) Empty() bool {return len(this.queue)==0
}

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

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

相关文章

什么是ACL?

知识改变命运,技术就是要分享,有问题随时联系,免费答疑,欢迎联系! 厦门微思网络​​​​​​https://www.xmws.cn 华为认证\华为HCIA-Datacom\华为HCIP-Datacom\华为HCIE-Datacom Linux\RHCE\RHCE 9.0\RHCA\ Oracle OC…

Skywalking的Trace Profiling 代码级性能剖析功能应用详解

代码级性能剖析 Skywalking 提供了Trace Profiling功能对具体出现问题的span进行代码级性能剖析。 代码级性能剖析就是利用方法栈快照,并对方法执行情况进行分析和汇总。并结合有限的分布式追踪 span 上下文,对代码执行速度进行估算。性能剖析激活时&a…

Linux系统管理和Shell脚本笔试题

1、写一个sed命令,修改/tmp/input.txt文件的内容,要求:(1) 删除所有空行;(2) 在非空行前面加一个"AAA",在行尾加一个"BBB",即将内容为11111的一行改为:AAA11111BBB #写入内…

自然语言处理(NLP)—— Dialogflow ES聊天机器人

1. 背景介绍 这个实验室的目标是让你了解并使用Google的Dialogflow服务。Dialogflow是一个可以让你创建聊天机器人的服务,这个过程不需要或者只需要很少的编程技能。 1.1 账号的创建 为了完成这个实验室,你需要在以下网站上创建账号&#xff1a…

STM32--USART串口(3)数据包

一、前言 在实际的工程中肯会有同时发送多种数据的情况,比如要不停的发送x、y、z分别对应三种不同的数据。xyzxyzxyz,但接收方可能是从中间某个地方开始接收的,这就导致数据错位。所以我们就需要将数据进行分割,打包成一个一个的…

Request Response 基础篇

Request & Response 在之前的博客中,初最初见到Request和Response对象,是在Servlet的Service方法的参数中,之前隐性地介绍过Request的作用是获取请求数据。通过获取的数据来进行进一步的逻辑处理,然后通过对Response来进行数…

如何搭建私有云盘SeaFile并实现远程访问本地文件资料

🌈个人主页: Aileen_0v0 🔥热门专栏: 华为鸿蒙系统学习|计算机网络|数据结构与算法 ​💫个人格言:“没有罗马,那就自己创造罗马~” #mermaid-svg-hsDnDEybLME85dTx {font-family:"trebuchet ms",verdana,arial,sans-serif;font-siz…

MySQL 多表查询

重点: MySQL 的 三种安装方式:包安装,二进制安装,源码编译安装。 MySQL 的 基本使用 MySQL 多实例 DDLcreate alter drop DML insert update delete DQL select 3.5)DDL 语句 表:二维关系 设计表&…

python计算两个DataFrame的指定两列中,相同的数据有多少

目的:查询数据1和数据2中,red与red列相同 并且blue与blue列相同的,情况有多少。 (备注:两个数据中格式不一致,需要经过json提取等处理步骤) 思路步骤: 1、读取数据1,筛选…

跨平台开发:浅析uni-app及其他主流APP开发方式

随着智能手机的普及,移动应用程序(APP)的需求不断增长。开发一款优秀的APP,不仅需要考虑功能和用户体验,还需要选择一种适合的开发方式。随着技术的发展,目前有多种主流的APP开发方式可供选择,其…

OfficeWeb365 Readfile 任意文件读取漏洞

免责声明:文章来源互联网收集整理,请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均由使用者本人负责,所产生的一切不良后果与文章作者无关。该…

使用Eclipse搞Android项目报错

相信现在都没什么人还会用Eclipse来开发的了。 不过安装完后,打开Eclipse会提示我的Jdk版本不符合 --------------------------- Incompatible JVM --------------------------- Version 1.8.0_391 of the JVM is not suitable for this product. Version: 17 or g…

PHP中的stdClass:一个动态的空白板

PHP中的stdClass:一个动态的空白板 在PHP编程中,灵活性和动态性是开发人员追求的重要目标。而stdClass作为PHP中的一个特殊类,为我们提供了一个通用的空白板,允许在运行时动态地添加属性和方法。它的存在为处理动态数据结构和临时…

MySQL的ACID、死锁、MVCC问题

1 ACID ACID代表原子性(atomicity)、一致性(consistency)、隔离性(isolation)和持久性(durability)。一个确保数据安全的事务处理系统,必须满足这些密切相关的标准。 原…

docker集成 nacos/nacos-server (包括踩的坑)

tips 这边需要的数据库我已经安装好了,所以数据库的安装这边已经省略了 拉取镜像(这边使用nacos1.4.1作为例子) docker pull nacos/nacos-server:1.4.1创建映射的文件夹 (conf存放配置文件,logs存放日志文件) mkdir -p /data/n…

java常量和kotlin常量

在java中使用final声明常量在kotlin中使用const val声明常量 常量在编译为字节码后会直接把调用常量的地方直接替换为常量值,示例如下: public class ConstDemo {public static final String NAME "Even";private static final int ID 100…

Python爬虫http基本原理

Python爬虫逆向系列(更新中):http://t.csdnimg.cn/5gvI3 HTTP 基本原理 在本节中,我们会详细了解 HTTP 的基本原理,了解在浏览器中敲入 URL 到获取网页内容之间发生了什么。了解了这些内容,有助于我们进一…

蓝桥杯-常用STL(一)

常用STL 🎈1.动态数组🎈2.vector的基础使用🔭2.1引入库🔭2.2构造一个动态数组🔭2.3插入元素🔭2.4获取长度并且访问元素🔭2.5修改元素🔭2.6删除元素🔭2.7清空 &#x1f38…

【力扣白嫖日记】SQL

前言 练习SQL语句,所有题目来自于力扣(https://leetcode.cn/problemset/database/)的免费数据库练习题。 今日题目: 1387.使用唯一标识码替代员工ID 表:Employees 列名类型idintnamevarchar 在 SQL 中&#xff0c…

c#的反汇编对抗

文章目录 前记nim攻防基础FFI内存加载加解密、编码 后记C#类型转换表nim基础 前记 随便编写一个c#调用winapi并用vs生成dll,同时用csc生成exe using System; using System.Runtime.InteropServices; namespace coleak {class winfun{[DllImport("User32.dll")]publ…