Day10 栈与队列 part01

53 阅读4分钟

概念

队列是先进先出,栈是先进后出

go语言实现栈与队列主要用到append 和 切片

var s []int

入栈:s = append(s, x) // x 为int

出栈: s = s[:len(s)-1]

var p []int

入队列:p = append(p, x)

出队列:p = p[1:]

刷题

232.用栈实现队列

leetcode.cn/problems/im…

使用两个栈实现队列的操作,一个名为 strack_input, 另一个命名为 strack_output,strack_input 负责接收push到队列的值, strack_output负责输出队列头。因为队列是先入先出的。

232.用栈实现队列版本2

type MyStrack []int

func (this *MyStrack) Push(x int) {

*this = append(*this, x)

}

  


func (this *MyStrack) Pop() int {

res := (*this)[this.Size()-1]

*this = (*this)[:this.Size()-1]

return res

}

  


func (this *MyStrack) Peek() int {

res := (*this)[this.Size()-1]

return res

}

  


func(this *MyStrack) Size() int{

return len(*this)

}

  


func(this *MyStrack) Is_Empty() bool {

return this.Size() == 0

}

  


type MyQueue struct {

strack_input *MyStrack

strack_output *MyStrack

}

  
  


func Constructor() MyQueue {

return MyQueue{

strack_input: &MyStrack{},

strack_output: &MyStrack{},

}

}

  
  


func (this *MyQueue) Push(x int) {

this.strack_input.Push(x)

}

  
  


func (this *MyQueue) Pop() int {

this.FillStrackOutput()

return this.strack_output.Pop()

}

  
  


func (this *MyQueue) Peek() int {

this.FillStrackOutput()

return this.strack_output.Peek()

}

  
  


func (this *MyQueue) Empty() bool {

return this.strack_input.Is_Empty() && this.strack_output.Is_Empty()

}

  


func (this *MyQueue) FillStrackOutput() {

if this.strack_output.Size() == 0 {

for this.strack_input.Size() != 0 {

pop := this.strack_input.Pop()

this.strack_output.Push(pop)

}

}

}

  
  


/**

* 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();

*/
225.用队列实现栈

leetcode.cn/problems/im…

image.png

这道题使用双队列来实现栈的操作,在golang的系统库中不支持队列与栈,这里创建两个struct,来实现队列。

解题思路:

队列是先进先出的,栈是先进后出的,在栈的结构体中定义两个队列的结构体,第一个队列负责接收输入的值,在pop操作中,首先需要检查第一个队列是否有值,如果没有值,两个队列互换,若第一个队列仍然没有值,panic。如果有值,将第一个队列的值按顺序导入第二个队列,当只剩最后一个元素的时候,删掉该元素并返回。

20.有效括号

leetcode.cn/problems/va…

image.png

这道题是用栈解决的经典问题

解题思路:

将由括号组成的string按照rune遍历,将每一个rune压入栈中(这里的栈使用golang的切片实现的,没有实现单独的栈结构体),将左侧符号的rune压入栈,遍历到右侧符号的时候,先检测栈是否为空,为空返回false,不为空则取出栈顶元素,进行匹配,匹配一致删除栈顶元素,直到遍历完成。判断stack是否有剩余元素,有返回false,没有返回true

1047. 删除字符串中所有相邻重复项

leetcode.cn/problems/re…

image.png

这道题也是利用栈来处理,这里引用一句话,感觉写的很好 要知道栈为什么适合这种爱消除的操作,因为栈可以记录遍历的元素的上一个元素

解题思路:

将string遍历,通过rune或者byte来遍历,因为这里不涉及到中文字符,先检测栈是否为空,如果是空执行压栈操作,不是空取出栈顶元素和当前遍历元素作比较,一致取出栈顶元素,不一致执行压栈操作。

总结

  1. 总结一下遍历字符串的几种方式

如果你只是想简单地遍历字符串中的每个字节,可以使用循环:

s := "Hello, 世界"
for i := 0; i < len(s); i++ {
    fmt.Printf("%x ", s[i]) // 打印每个字节的十六进制表示
}

或者使用range关键字:

s := "Hello, 世界"
for index, runeValue := range s {
    fmt.Printf("%d: %x\n", index, runeValue) // 打印每个字符的Unicode码点和索引
}

注意:在上面的range示例中,runeValue的类型是rune,它是Go语言中用于表示一个Unicode码点的类型。这意味着这种方式可以正确处理多字节字符(如UTF-8编码中的汉字),但如果你只想按字节遍历,应该使用第一种方法。

  1. 计算机在执行代码的时候也存在使用栈的情况

比如递归的实现:每一次递归操作都是把当前函数的局部变量,参数值 和返回地址压入调用栈中,当递归返回的时候,会从栈顶弹出上一次递归的参数,这就是为什么递归操作总能返回上一层函数地址的原因了

相信大家应该遇到过一种错误就是栈溢出,系统输出的异常是Segmentation fault(当然不是所有的Segmentation fault 都是栈溢出导致的) ,如果你使用了递归,就要想一想是不是无限递归了,那么系统调用栈就会溢出。

而且在企业项目开发中,尽量不要使用递归!在项目比较大的时候,由于参数多,全局变量等等,使用递归很容易判断不充分return的条件,非常容易无限递归(或者递归层级过深),造成栈溢出错误(这种问题还不好排查!)