LeetCode232.用栈实现队列
1. 思路
一个输入栈,一个输出栈。
输入栈负责不停的push元素进去
输出栈有两种情况:
一,当输出栈(out stack)为空时,说明还没有调用任何的push或者peek操作,此时如果要进行push或者peek,首先要把输入栈(in stack)里面的所有元素pop出来,依次放入到输出栈里面,直到输入栈为空。接着我们返回输出栈的栈顶元素即可。
例如:in stack: 12345,out stack: empty,此时就要不停的把in stack元素pop出来,放入out stack,变成 in stack: empty, out stack: 54321, 最后返回out stack栈顶元素
二,当输出栈 (out stack) 不为空时,说明我们已经调用过push或者peek的操作了,那么此时无论in stack有没有继续掉用过push操作,我们都能够保证:out stack的栈顶元素一定就是最先入栈的那个元素。那么此时我们就不需要把 in stack元素放进 out stack,而是直接返回 out stack的栈顶元素即可。
4.简单的来说,这两种情况就可以归结为一种:检查out stack是否为空,如果是空,那么就把 in stack里面的所有元素倒进 out stack,最后返回 out stack 栈顶元素。如果out stack不为空,直接返回out stack 栈顶元素。
2.时空复杂度:
时间复杂度:push, empty 为O(1),peek和pop为均摊时间复杂度为O(1),因为每个元素至多入栈和出栈2次。-- 什么是均摊时间复杂度?因为这个题目里面,是否要把in stack元素全部倒入 out stack,以及倒入元素是多少,这个情况是不确定的,最坏情况下,每个元素会经历入栈和出栈的两次操作,那么其实均摊下来对于这个元素而言,时间复杂度就是O(1)
空间复杂度:空间复杂度在最坏情况下需要存储n个元素,因此是 O(N)
代码
class MyQueue:
def __init__(self):
self.stackIn = [] # 元素负责进栈
self.stackOut = [] # 元素负责出栈
def push(self, x: int) -> None: # push - O(1)
self.stackIn.append(x)
def pop(self) -> int:
if not self.stackOut:
while self.stackIn:
self.stackOut.append(self.stackIn.pop())
return self.stackOut.pop()
def peek(self) -> int:
if not self.stackOut:
while self.stackIn:
self.stackOut.append(self.stackIn.pop())
return self.stackOut[-1]
def empty(self) -> bool: # empty - O(1)
if not self.stackOut and not self.stackIn:
return True
return False
LeetCode225. 用队列实现栈
1. 思路
使用一个队列实现就够了。例如:12345
如果调用pop函数,那么保留队尾元素,变成 5 | 1234 接着popleft,就把5这个栈顶元素pop出来了
如果调用peek函数,那么直接返回队列的最后一个元素即可。(这个题比上面那个题简单多了)
2.代码
class MyStack(object):
def __init__(self):
self.q = collections.deque()
def push(self, x):
"""
:type x: int
:rtype: None
"""
self.q.append(x)
def pop(self):
"""
:rtype: int
"""
n = len(self.q)
for i in range(n-1): # 巧妙操作:通过计算queue的长度来保证不pop最后一个元素
self.q.append(self.q.popleft())
return self.q.popleft()
def top(self):
"""
:rtype: int
"""
return self.q[-1]
def empty(self):
"""
:rtype: bool
"""
if len(self.q) == 0:
return True
else:
return False
复杂度:
入栈self.push()
时间复杂度O(1),直接把元素append到queue中的最后就行;
空间复杂度O(1),queue 栈多储存一个元素;
出栈self.pop()# 只有pop操作的时间复杂度是O(N),因为需要把前面的n-1个元素重新入队
时间复杂度O(N),需要把除最后一个元素之外的所有元素全部弹出再压入队列中
空间复杂度O(N)需要额外的N个内存来储存栈中的元素;
取栈首元素self.top()
直接用deque的-1下标,时间复杂度O(1);空间复杂度O(1);
判断空self.empty()
只需要判断队列是否为空即可,时间复杂度O(1),空间复杂度O(1)
LeetCode20. 有效的括号
思路
先来分析一下 这里有三种不匹配的情况:(图片文字来自@努力学习的牛西宁)
第一种情况,字符串里左方向的括号多余了 ,所以不匹配。
第二种情况,括号没有多余,但是 括号的类型没有匹配上。
第三种情况,字符串里右方向的括号多余了,所以不匹配。
class Solution(object):
def isValid(self, s):
stack = []
if not s:
return True
for item in s:
if item == "(":
stack.append(")")
elif item == "[":
stack.append("]")
elif item == "{":
stack.append("}")
elif not stack or item!=stack[-1]: # 如果栈提前变空或者栈顶元素和字符串元素不匹配
return False
else: # 如果栈顶元素和字符串相同,直接pop掉栈顶元素
stack.pop()
return not stack # 最后如果遍历完字符串,栈内为空,说明匹配成功
3. 复杂度分析
时间复杂度:O(N)
字符串长度为N,里面的元素需要都遍历一遍,因此时间复杂度为O(N);
空间复杂度:O(N)
需要一个栈来储存元素,大小是O(N)级别的,总体来说的空间复杂度也为O(N)。
LeetCode1047. 删除字符串中的所有相邻重复项
链接:1047. 删除字符串中的所有相邻重复项 - 力扣(LeetCode)
思路
这个题就是有效的括号变题,感觉不用多讲了吧。如果栈不为空,同时栈顶元素和字符串当前元素相同,那么直接把栈顶元素弹出,否则就不停的往站内append元素即可。这个题和google面试的 decode string题目异曲同工!
2.代码
class Solution(object):
def removeDuplicates(self, s):
"""
:type s: str
:rtype: str
"""
stack = []
for char in s:
if stack and stack[-1] == char:
stack.pop()
else:
stack.append(char)
return ''.join(stack)
3.思考
栈适合用来解决匹配的问题
谷歌面试有什么陨石碰撞,石头重量相同就粉碎这种,其实本质上就是栈的匹配问题。
重新回来刷题的思考:2023年2月13号。
行百里者半九十,没想到自己竟然也成为了茫茫大军中放弃的一个人。归根结底还是因为1月13号收到谷歌实习面试的拒信那天,给了我挺大的心理打击。从那天之后,我就没有碰过leetcode,没有认真刷题,在这一个月里,天天幻想着能够有白马王子来解决自己的身份问题。但我从来就知道,最稳的方法从来就是自己要变强,求别人的永远都会内心不安,从而也不曾得到。靠我投机取巧得来的面试机会,也靠着我的实力不足给完美的错过了。如果说去年面试大厂实习全军覆没是因为自己实力不足,project根本没有做,题目也没有吃透,面试的时候紧张。那么说今年能不能在未知的情况下尽可能把自己这边先做好,问心无愧,再去抱怨说生活的苦,我觉得这是我这个spring学期所要面对的功课。我要恭喜自己,终于分手成功,离开了一个并不适合自己的人。女生还是要学会独立。要学会开车。当年感觉学gre很累,很辛苦,但其实每天安排一点点学习时间,学出来也就是3个月的时间,真的没有很久。我觉得自己也应该要对自己的人生负责,不要想着什么男人,不要想着什么婚姻绿卡,把自己的实力真真正正的提高,不要投机取巧的方式,才是最快的提高方法。加油吧,日子还很长。努力。