理论基础(摘自代码随想录)
C++标准库是有多个版本的,要知道我们使用的STL是哪个版本,才能知道对应的栈和队列的实现原理。
那么来介绍一下,三个最为普遍的STL版本:
-
HP STL 其他版本的C++ STL,一般是以HP STL为蓝本实现出来的,HP STL是C++ STL的第一个实现版本,而且开放源代码。
-
P.J.Plauger STL 由P.J.Plauger参照HP STL实现出来的,被Visual C++编译器所采用,不是开源的。
-
SGI STL 由Silicon Graphics Computer Systems公司参照HP STL实现,被Linux的C++编译器GCC所采用,SGI STL是开源软件,源码可读性甚高。
接下来介绍的栈和队列也是SGI STL里面的数据结构, 知道了使用版本,才知道对应的底层实现。
来说一说栈,栈先进后出,如图所示:
栈提供push 和 pop 等等接口,所有元素必须符合先进后出规则,所以栈不提供走访功能,也不提供迭代器(iterator)。 不像是set 或者map 提供迭代器iterator来遍历所有元素。
栈是以底层容器完成其所有的工作,对外提供统一的接口,底层容器是可插拔的(也就是说我们可以控制使用哪种容器来实现栈的功能)。
所以STL中栈往往不被归类为容器,而被归类为container adapter(容器适配器)。
那么问题来了,STL 中栈是用什么容器实现的?
从下图中可以看出,栈的内部结构,栈的底层实现可以是vector,deque,list 都是可以的, 主要就是数组和链表的底层实现。
我们常用的SGI STL,如果没有指定底层实现的话,默认是以deque为缺省情况下栈的低层结构。
deque是一个双向队列,只要封住一段,只开通另一端就可以实现栈的逻辑了。
SGI STL中 队列底层实现缺省情况下一样使用deque实现的。
我们也可以指定vector为栈的底层实现,初始化语句如下:
std::stack<int, std::vector<int> > third; // 使用vector为底层容器的栈
刚刚讲过栈的特性,对应的队列的情况是一样的。
队列中先进先出的数据结构,同样不允许有遍历行为,不提供迭代器, SGI STL中队列一样是以deque为缺省情况下的底部结构。
也可以指定list 为起底层实现,初始化queue的语句如下:
std::queue<int, std::list<int>> third; // 定义以list为底层容器的队列
所以STL 队列也不被归类为容器,而被归类为container adapter( 容器适配器)。
232.用栈实现队列
python 中可以直接用list来模拟栈stack, list可以用pop() 和 append()来模拟出栈入栈
这道题用栈实现队列, 栈是先入后出, 队列是先入先出的,思路就可以用两个栈stack_in来实现push,然后再把stack_in中的元素加进第二个栈stack_out中,stack_out就负责出栈。只要stack_in和stack_out不为空,那么整个模拟的队列就不为空
push函数:只要有新元素进来,就往stack_in中加
pop函数:如果模拟的队列是空的,return None; 如果stack_out是空的,就把stack_in的元素往里pop,然后stack_out pop, 如果stack_out有元素就pop
peek函数:找队首元素,其实就是找stack_out中的元素,可以直接调用pop函数,但是要记得补上
class MyQueue:
def __init__(self):
"""
in主要负责push,out主要负责pop
"""
self.stack_in = []
self.stack_out = []
def push(self, x: int) -> None:
"""
有新元素进来,就往in里面push
"""
self.stack_in.append(x)
def pop(self) -> int:
"""
Removes the element from in front of queue and returns that element.
"""
if self.empty():
return None
if self.stack_out:
return self.stack_out.pop()
else:
for i in range(len(self.stack_in)):
self.stack_out.append(self.stack_in.pop())
return self.stack_out.pop()
def peek(self) -> int:
"""
Get the front element.
"""
ans = self.pop()
self.stack_out.append(ans)
return ans
def empty(self) -> bool:
"""
只要in或者out有元素,说明队列不为空
"""
return not (self.stack_in or self.stack_out)
# Your MyQueue object will be instantiated and called as such:
# obj = MyQueue()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.peek()
# param_4 = obj.empty()
225. 用队列实现栈
用一个队列模拟栈,在python中可以用双向队列deque()
push就是正常加进队列
pop 就是把队列的前n-1个元素弹出,然后加进队尾,再弹出就是栈的出栈顺序了
class MyStack:
def __init__(self):
self.queue = deque()
def push(self, x: int) -> None:
self.queue.append(x)
def pop(self) -> int:
if not self.queue: return None
for i in range(len(self.queue)-1): #把队列前n-1个元素弹出来再加到队尾(右)
self.queue.append(self.queue.popleft())
return self.queue.popleft() #然后再popleft就和栈的弹出顺序一样了
def top(self) -> int:
return self.queue[-1] #栈顶的话 对应的应该是最后进来的那个元素,先入后出嘛,在队列里就是队尾(右边)
def empty(self) -> bool:
return not self.queue
20. 有效的括号
这道题是用栈的
这里有三种不匹配的情况,
-
第一种情况,字符串里左方向的括号多余了 ,所以不匹配。
-
第二种情况,括号没有多余,但是 括号的类型没有匹配上。
第一种情况:已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false |
第二种情况:遍历字符串匹配的过程中,发现栈里没有要匹配的字符。所以return false |
第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号return false
什么时候说明左括号和右括号全都匹配了呢,就是字符串遍历完之后,栈是空的,就说明全都匹配了。
如下图代码所示:
在遍历的过程中,我们遇到左括号,就把对应的右括号放入栈中
然后遍历到右括号的时候,如果说栈不为空,并且栈顶元素等于此时遍历的元素,说明配对了,消消乐
else省略的条件是什么呢?是栈为空,代表右括号多,或者stack栈顶元素不匹配,return False
class Solution:
def isValid(self, s: str) -> bool:
stack = []
for item in s:
if item == '(': stack.append(')')
elif item == '[': stack.append(']')
elif item == '{': stack.append('}')
elif stack and stack[-1] == item:
stack.pop()
else:
return False
return not stack
class Solution:
def isValid(self, s: str) -> bool:
dic = {'(':')','[':']','{':'}'}
stack = []
for ch in s:
if ch in dic.keys():
stack.append(dic[ch])
elif stack and ch == stack[-1]:
stack.pop()
else:
return False
return not stack
1047. 删除字符串中的所有相邻重复项
这个题的拿栈去做的思想,类似于加入的元素如果等于栈顶就可以消消乐的感觉
就是拿一个list去模拟栈,然后在遍历s的时候,如果栈为空或者栈顶元素不等于当前遍历到的元素,就把该元素入栈,如果栈不为空并且栈顶元素等于遍历到的当前元素的话,就出栈消消乐
注意,在拿list去模拟的时候,要注意哪里是头哪里是尾,list的pop是pop的尾部,添加的元素也是从尾部添加的。最后返回出list其实就是题要求的结果
def removeDuplicates(self, s: str) -> str:
stack = []
for i in s:
if not stack or stack[-1] != i:
stack.append(i)
else:
stack.pop()
return ''.join(stack)
def removeDuplicates(self, s: str) -> str:
stack = []
for i in s:
if stack and stack[-1] == i:
stack.pop()
else:
stack.append(i)
return ''.join(stack)