概念
队列是先进先出,栈是先进后出
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.用栈实现队列
使用两个栈实现队列的操作,一个名为 strack_input, 另一个命名为 strack_output,strack_input 负责接收push到队列的值, strack_output负责输出队列头。因为队列是先入先出的。
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.用队列实现栈
这道题使用双队列来实现栈的操作,在golang的系统库中不支持队列与栈,这里创建两个struct,来实现队列。
解题思路:
队列是先进先出的,栈是先进后出的,在栈的结构体中定义两个队列的结构体,第一个队列负责接收输入的值,在pop操作中,首先需要检查第一个队列是否有值,如果没有值,两个队列互换,若第一个队列仍然没有值,panic。如果有值,将第一个队列的值按顺序导入第二个队列,当只剩最后一个元素的时候,删掉该元素并返回。
20.有效括号
这道题是用栈解决的经典问题
解题思路:
将由括号组成的string按照rune遍历,将每一个rune压入栈中(这里的栈使用golang的切片实现的,没有实现单独的栈结构体),将左侧符号的rune压入栈,遍历到右侧符号的时候,先检测栈是否为空,为空返回false,不为空则取出栈顶元素,进行匹配,匹配一致删除栈顶元素,直到遍历完成。判断stack是否有剩余元素,有返回false,没有返回true
1047. 删除字符串中所有相邻重复项
这道题也是利用栈来处理,这里引用一句话,感觉写的很好 要知道栈为什么适合这种爱消除的操作,因为栈可以记录遍历的元素的上一个元素
解题思路:
将string遍历,通过rune或者byte来遍历,因为这里不涉及到中文字符,先检测栈是否为空,如果是空执行压栈操作,不是空取出栈顶元素和当前遍历元素作比较,一致取出栈顶元素,不一致执行压栈操作。
总结
- 总结一下遍历字符串的几种方式
如果你只是想简单地遍历字符串中的每个字节,可以使用循环:
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编码中的汉字),但如果你只想按字节遍历,应该使用第一种方法。
- 计算机在执行代码的时候也存在使用栈的情况
比如递归的实现:每一次递归操作都是把当前函数的局部变量,参数值 和返回地址压入调用栈中,当递归返回的时候,会从栈顶弹出上一次递归的参数,这就是为什么递归操作总能返回上一层函数地址的原因了
相信大家应该遇到过一种错误就是栈溢出,系统输出的异常是Segmentation fault(当然不是所有的Segmentation fault 都是栈溢出导致的) ,如果你使用了递归,就要想一想是不是无限递归了,那么系统调用栈就会溢出。
而且在企业项目开发中,尽量不要使用递归!在项目比较大的时候,由于参数多,全局变量等等,使用递归很容易判断不充分return的条件,非常容易无限递归(或者递归层级过深),造成栈溢出错误(这种问题还不好排查!)