算法day9

news2024/11/20 14:21:02

算法day9

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

栈与队列理论基础

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

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

package main

import "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 main

import "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 Queue

    q.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 []int
    stackout []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.coloradmin.cn/o/1427897.html

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

相关文章

Linux第41步_移植ST公司uboot的第2步_修改网络驱动_USB OTG设备树_LCD驱动_以及编译和烧写测试

移植ST公司uboot的第1步,创建配置文件、设备树、修改电源管理和sdmmc节点后,还需要进一部修改,如:网络驱动、USB OTG设备树、LCD驱动,以及编译和烧写测试。 一、在虚拟机中,使用VSCode打开my_uboot工作区 …

1E,Jarvis March

四个问题: 一,Jarvis March算法借鉴了什么算法? 二,如何确定初始点 三,如何获取凸包的边? 四,Jarvis March算法的好处在哪里? 首先看第一个问题, 一,Jarvis …

Springboot 自定义参数配置化,密钥,密码,文件保存路径

application.properties 和 application.yml 都是一样的配置方法,只是格式不一样 定义配置文件 server.port8080 image.save.pathE:\ #自定义文件保存路径读取配置文件 Value("${image.save.path}")private String filePath;//E:\优化配置文件 如果我参…

算法——A/算法通识

目录 一、复杂度分析 A/时间复杂度 B/空间复杂度 C/分析技巧 二、枚举分析 A/枚举算法介绍 B/解空间的类型 C/循环枚举解空间 三、模拟算法 四、递归 A/递归介绍 递归的两个关键要素: B/递归如何实现 C/递归和循环的比较 一、复杂度分析 A/时间复杂度…

Unknown custom element:<xxx>-did you register the component correctly解决方案

如图所示控制台发现了爆红(大哭): 报错解释: 当我们看到报错时,我们需要看到一些关键词,比如显眼的“component”和“name”这两个单词, 因此我们就从此处切入,大概与组件有关系。…

Transition内置组件设置无效的原因

1. 包裹的组件是否有显示隐藏状态的切换&#xff0c;或者是绑定的key值是否发生改变 由 v-if 所触发的切换由 v-show 所触发的切换由特殊元素<component> 切换的动态组件改变特殊的key属性 2. 要放在发生变化的组件外层&#xff0c;如果中间有其他元素或组件会不生效 …

C++ copy()函数详细介绍

copy() 是一个标准库函数&#xff0c;位于 头文件中。它用于将一个容器中的元素复制到另一个容器中&#xff0c;或者将一个范围内的元素复制到另一个范围中。 函数参数介绍 copy( first, last, d_first );first 和 last&#xff1a;表示输入范围的迭代器。 first 指向要复制的…

Keil软件某些汉字输出乱码,0xFD问题,51单片机

1. 问题 keil软件输入某些汉字的时候会输出乱码&#xff0c;例如&#xff1a;升、 数 2. 原因 keil软件会忽略0xFD。 升的GB2312编码为 0xc9fd&#xff0c;keil解析为0xc9数的GB2312编码为 0xcafd&#xff0c;keil解析为0xca 关于Keil软件中0xFD问题的说明 3. 解决方案1 …

深入解剖指针篇(3)

个人主页&#xff08;找往期文章&#xff09; &#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 目录 二级指针 指针数组 指针数组模拟二维数组 字符指针变量 数组指针 数组指针初始化 二维数组传参的本质 函数指针 函数指针的使用 typedef关键字 函数指针数组 二级指针…

Linux内存管理:(十一)页面分配之慢速路径

文章说明&#xff1a; Linux内核版本&#xff1a;5.0 架构&#xff1a;ARM64 参考资料及图片来源&#xff1a;《奔跑吧Linux内核》 Linux 5.0内核源码注释仓库地址&#xff1a; zhangzihengya/LinuxSourceCode_v5.0_study (github.com) 1. 水位管理和分配优先级 页面分配…

0202-2-存储器管理

第四章:存储器管理 存储器的层次结构 多层结构的存储系统 存储器的多层结构 CPU寄存器主存辅存 可执行存储器 寄存器和主存的总称访问速度快&#xff0c;进程可以在很少的时钟周期内用一条load或store指令完成存取。 主存储器与寄存器 高速缓存和磁盘缓存 程序的装入和链…

《金融时报》:直面“雪球”风波 究竟影响几何?

“他们给我推荐的时候说是只要市场不大跌&#xff0c;我就能按照年化20%获得收益&#xff0c;当时我看大盘走势&#xff0c;也认为跌那么多的概率不大。”李先生告诉《金融时报》记者&#xff0c;他当初被银行客户经理推荐“雪球”产品并头脑一热买了的时候&#xff0c;以为按照…

springboot集成 mysql快速入门demo

一、mysql环境搭建 采用docker-compose搭建&#xff0c;配置如下&#xff1a; docker-compose.yml version: 3 services:mysql:image: registry.cn-hangzhou.aliyuncs.com/zhengqing/mysql:5.7 # 原镜像mysql:5.7container_name: mysql_3306 …

C++入坑基础知识点

当学习了C语言之后&#xff0c;很多的小伙伴都想进一步学习C&#xff0c;但两者有相当一部分的内容都是重叠的&#xff0c;不知道该从哪些方面开始入门C&#xff0c;这篇文章罗列了从C到C必学的入门知识&#xff0c;学完就算是踏入C的大门了。 1. 命名空间 写C的时候&#xff…

LeetCode 热题 100 | 链表(上)

目录 1 基础知识 1.1 空指针 1.2 结构体 1.3 指针访问 1.4 三目运算符 2 160. 相交链表 3 206. 反转链表 4 234. 回文链表 菜鸟做题第三周&#xff0c;语言是 C 1 基础知识 1.1 空指针 使用 nullptr 来判断是否为空指针&#xff1a; if (headA nullptr) …

鸿蒙开发有必要学吗?看完这篇再决定吧

在科技的潮流中&#xff0c;每一次新操作系统的诞生都是对旧秩序的挑战与新机遇的孕育。鸿蒙操作系统的出现&#xff0c;无疑是近年来科技界最引人注目的事件之一。自华为于2019年正式推出鸿蒙系统以来&#xff0c;这一我们自主研发的操作系统不仅在国内引起巨大反响&#xff0…

常见的6种软件测试用例设计方法

常见的软件测试用例设计方法&#xff0c;个人认为主要是下面这6种&#xff1a; 流程图法&#xff08;也叫场景法&#xff09;等价类划分法边界值分析判定表正交法错误推测法 这6种常见方法中&#xff0c;我分别按照定义、应用场景、使用步骤、案例讲解这4个部分进行讲解。 所…

MySQL查询数据(十)

MySQL查询数据&#xff08;十&#xff09; 一、SELECT基本查询 1.1 SELECT语句的功能 SELECT 语句从数据库中返回信息。使用一个 SELECT 语句&#xff0c;可以做下面的事&#xff1a; **列选择&#xff1a;**能够使用 SELECT 语句的列选择功能选择表中的列&#xff0c;这些…

不一样的味觉体验:精酿啤酒与烤肉的绝妙搭配

在繁华的都市生活中&#xff0c;人们总是在寻找那份与众不同的味觉享受。当夏日的微风轻轻拂过&#xff0c;你是否想过&#xff0c;与三五好友围坐在一起&#xff0c;拿着Fendi Club啤酒与烤肉的绝妙搭配&#xff0c;畅谈生活点滴&#xff0c;感受那份惬意与自在&#xff1f; F…

2401Idea用GradleKotlin编译Java控制台中文出乱码解决

解决方法 解决方法1 在项目 build.gradle.kts 文件中加入 tasks.withType<JavaCompile> {options.encoding "UTF-8" } tasks.withType<JavaExec> {systemProperty("file.encoding", "utf-8") }经测试, 只加 tasks.withType<…