基础概念
队列:先进先出,跟排队一样。
栈:先进后出,后进先出,跟叠盘子和拿盘子一样。
232.用栈实现队列
使用栈实现队列的下列操作:
push(x) -- 将一个元素放入队列的尾部。
pop() -- 从队列首部移除元素。
peek() -- 返回队列首部的元素。
empty() -- 返回队列是否为空。
分析:
用两个栈来实现队列。因为队列是先进先出,而栈是先进后出,所以我们可以想到先把输入先存进栈A,然后当有需要输出的时候,把栈A的内容全部存到栈B,然后从栈B弹出。这时候就顺序就是跟队列一样了。
举个例子:有两个栈分别是stackIn和stackOut
假设输入1、2、3,按照队列就要输出1、2、3。
我们用栈实现就是先把1、2、3输入到stackIn、然后再从stackIn输出到stackOut。然后再输出stackOut就实现了队列。记得每次要判断和更新各自的栈顶。
所以本题代码如下:
// MyQueue 队列类
type MyQueue struct {
stackIn []int
stackOut []int
}
// Constructor 队列类的构造方法
func Constructor() MyQueue {
stackIn := make([]int, 0)
stackOut := make([]int, 0)
return MyQueue{stackIn, stackOut}
}
// Push 入队
func (this *MyQueue) Push(x int) {
// 向输入栈添加元素
this.stackIn = append(this.stackIn, x)
}
// Pop 出队
func (this *MyQueue) Pop() int {
result := 0
// 输出栈为空,将输入栈全部元素移到输出栈
if len(this.stackOut) == 0 {
for len(this.stackIn) != 0 {
// 取出输入栈栈顶元素
temp := this.stackIn[len(this.stackIn)-1]
this.stackIn = this.stackIn[:len(this.stackIn)-1] //更新stackIn的栈顶元素
// 将从输入栈取出的元素放入输出栈
this.stackOut = append(this.stackOut, temp)
}
}
// 取出输出栈栈顶元素
result = this.stackOut[len(this.stackOut)-1]
this.stackOut = this.stackOut[:len(this.stackOut)-1] //更新stackOut的栈顶元素
return result
}
// Peek 返回队列头元素,就是stackOut的栈顶
func (this *MyQueue) Peek() int {
result := 0
// 输出栈为空,将输入栈全部元素移到输出栈
if len(this.stackOut) == 0 {
for len(this.stackIn) != 0 {
// 取出输入栈栈顶元素
temp := this.stackIn[len(this.stackIn)-1]
this.stackIn = this.stackIn[:len(this.stackIn)-1]
// 将从输入栈取出的元素放入输出栈
this.stackOut = append(this.stackOut, temp)
}
}
// 得到输出栈栈顶元素
result = this.stackOut[len(this.stackOut)-1]
return result
}
// Empty 判断队空
func (this *MyQueue) Empty() bool {
if len(this.stackIn) == 0 && len(this.stackOut) == 0 {
return true
}
return false
}
扩展:
可以看出peek()的实现,直接复用了pop(), 要不然,对stOut判空的逻辑又要重写一遍。
再多说一些代码开发上的习惯问题,在工业级别代码开发中,最忌讳的就是 实现一个类似的函数,直接把代码粘过来改一改就完事了。
这样的项目代码会越来越乱,一定要懂得复用,功能相近的函数要抽象出来,不要大量的复制粘贴,很容易出问题!(踩过坑的人自然懂)
工作中如果发现某一个功能自己要经常用,同事们可能也会用到,自己就花点时间把这个功能抽象成一个好用的函数或者工具类,不仅自己方便,也方面了同事们。
同事们就会逐渐认可你的工作态度和工作能力,自己的口碑都是这么一点一点积累起来的!在同事圈里口碑起来了之后,你就发现自己走上了一个正循环,以后的升职加薪才少不了你!哈哈哈。
225. 用队列实现栈
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
实现 MyStack 类:
void push(int x) 将元素 x 压入栈顶。
int pop() 移除并返回栈顶元素。
int top() 返回栈顶元素。
boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。
思路:
其实这道题目就是用一个队列就够了。
一个队列在模拟栈弹出元素的时候只要将队列头部的元素(除了最后一个元素外) 重新添加到队列尾部,此时在去弹出元素就是栈的顺序了。
代码如下:
type MyStack struct {
queue []int//创建一个队列
}
/** Initialize your data structure here. */
func Constructor() MyStack {
return MyStack{ //初始化
queue:make([]int,0),
}
}
/** Push element x onto stack. */
func (this *MyStack) Push(x int) {
//添加元素
this.queue=append(this.queue,x)
}
/** Removes the element on top of the stack and returns that element. */
func (this *MyStack) Pop() int {
n:=len(this.queue)-1//判断长度
for n!=0{ //除了最后一个,其余的都重新添加到队列里
val:=this.queue[0]
this.queue=this.queue[1:]
this.queue=append(this.queue,val)
n--
}
//弹出元素
val:=this.queue[0]
this.queue=this.queue[1:]
return val
}
/** Get the top element. */
func (this *MyStack) Top() int {
//利用Pop函数,弹出来的元素重新添加
val:=this.Pop()
this.queue=append(this.queue,val)
return val
}
/** Returns whether the stack is empty. */
func (this *MyStack) Empty() bool {
return len(this.queue)==0
}
20. 有效的括号
给定一个只包括 '(',')','{','}','[',']' 的字符串 s ,判断字符串是否有效。
有效字符串需满足:
1、左括号必须用相同类型的右括号闭合。
2、左括号必须以正确的顺序闭合。
示例 1:
输入:s = "()"
输出:true
示例 2:
输入:s = "()[]{}"
输出:true
示例 3:
输入:s = "(]"
输出:false
思路:
开始接触题目时,我们会不禁想到如果计算出左括号的数量,和右括号的数量,如果每种括号左右数量相同,会不会就是有效的括号了呢?
事实上不是的,假如输入是 [{]},每种括号的左右数量分别相等,但不是有效的括号。这是因为结果还与括号的位置有关。
仔细分析我们发现,对于有效的括号,它的部分子表达式仍然是有效的括号,比如 {()[()]} 是一个有效的括号,()[{}] 是有效的括号,[()] 也是有效的括号。并且当我们每次删除一个最小的括号对时,我们会逐渐将括号删除完。比如下面的例子。
这个思考的过程其实就是栈的实现过程。因此我们考虑使用栈,当遇到匹配的最小括号对时,我们将这对括号从栈中删除(即出栈),如果最后栈为空,那么它是有效的括号,反之不是。
代码如下:
func isValid(s string) bool {
// 用字典事先存好括号的匹配规则
hashmap := map[byte]byte{
')': '(',
'}': '{',
']': '[',
}
// 用切片模拟栈
stack := make([]byte, 0)
if s == "" {
// 空串有效
return true
}
// 遍历字符串
for i := 0; i < len(s); i++ {
if s[i] == '(' || s[i] == '{' || s[i] == '[' {
// 遍历字符串时遇到左括号 压栈
stack = append(stack, s[i])
} else if len(stack) != 0 && stack[len(stack)-1] == hashmap[s[i]] {
// 遍历字符串时遇到右括号 栈非空且栈顶元素与该右括号匹配 弹栈
stack = stack[:len(stack)-1]
} else {
// 遍历字符串时遇到左括号 栈为空或栈顶元素与该右括号不匹配 证明有右括号多余
return false
}
}
if len(stack) != 0 {
// 遍历完了字符串,但是栈非空,证明有左括号多余
return false
} else {
return true
}
}
1047. 删除字符串中的所有相邻重复项
给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。
在 S 上反复执行重复项删除操作,直到无法继续删除。在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。
示例: 输入:"abbaca"
输出:"ca"
解释: 例如,在 "abbaca" 中,我们可以删除 "bb" 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 "aaca",其中又只有 "aa" 可以执行重复项删除操作,所以最后的字符串为 "ca"。
提示: 1 <= S.length <= 20000 S 仅由小写英文字母组成。
思路:
遍历字符串,先压入一个字符串到栈中,继续遍历,如果再遇到的字符串已经在栈中存在则删除掉栈中所存的.....不断这样遍历end即可
代码如下:
1.遍历字符串,先压入一个字符串到栈中,继续遍历,如果再遇到的字符串已经在栈中存在则删除掉栈中所存的.....不断这样遍历end即可
func removeDuplicates(s string) string {
var stack []rune
for _, v := range s {
if len(stack) != 0 && v == stack[len(stack)-1] {
stack = stack[:len(stack)-1]
continue
}
stack = append(stack, v)
}
return string(stack)
}