数据规模->时间复杂度
<=10^4 😮(n^2)
<=10^7:o(nlogn)
<=10^8:o(n)
10^8<=:o(logn),o(1)
总结(栈和队列)-操作受限的线性结构
'''
#基本操作
'''
def test(self) -> None:
stack = list()
# 压栈
stack.append(2)
stack.append(3)
# 栈顶元素
top = stack[-1]
# 栈是否为空
is_empty = not stack
# 出栈
stack.pop()
lc 20【top100】:有效的括号
https://leetcode.cn/problems/valid-parentheses/
提示:
1 <= s.length <= 104
s 仅由括号 ‘()[]{}’ 组成
#方案一:
class Solution:
def isValid(self, s: str) -> bool:
if len(s)%2 != 0:return False
#o(n),o(n)
stack=[]
for c in s:
if c=='('or c=='{' or c=='[':
stack.append(c)
else:
if not stack:return False
#
tp=stack.pop()
if c==')' and tp !='(':return False
if c=='}' and tp !='{':return False
if c==']' and tp !='[':return False
return not stack
#方案二:map扩展优化
class Solution:
def isValid(self, s: str) -> bool:
if len(s)%2 != 0:return False
#o(n),o(n)
stack=[]
mp={"(": ")","[": "]","{": "}"}
for c in s:
if c in mp:
stack.append(c)
else:
if not stack:return False
#
tp=stack.pop()
if c !=mp.get(tp):return False
return not stack
lc 71【剑指 017】 :简化路径
https://leetcode.cn/problems/simplify-path/
提示:
1 <= path.length <= 3000
path 由英文字母,数字,‘.’,‘/’ 或 ’ ’ 组成。
path 是一个有效的 Unix 风格绝对路径。
class Solution:
def simplifyPath(self, path: str) -> str:
s=[c for c in path.split("/") if c != "" and c !="."]#注意""
#
stack=[]
for c in s:
if c ==".." and not stack:continue
elif c==".." and stack:stack.pop()
else:stack.append(c)
#
return "/"+'/'.join(stack)#key:做双向队列处理
lc 394【top100】:字符串解码
https://leetcode.cn/problems/decode-string/
提示:
1 <= s.length <= 30
s 由小写英文字母、数字和方括号 ‘[]’ 组成
s 保证是一个 有效 的输入。
s 中所有整数的取值范围为 [1, 300]
class Solution:
def decodeString(self, s: str) -> str:
#o(n)
res=''
numstack=[]
strstack=[]
#o(n)
num=0
for c in s:
if c>='0' and c<='9':
num=num*10+ord(c)-ord('0')
elif c=='[':#压栈标志
numstack.append(num)
strstack.append(res)
num,res=0,''
elif c==']':#出栈标志
items=res#出发点
for i in range(1,numstack.pop()):
res+=items
res=strstack.pop()+res
else:res+=c
return res
lc 224:基本计算器
https://leetcode.cn/problems/basic-calculator/
提示:
1 <= s.length <= 3 * 105
s 由数字、‘+’、‘-’、‘(’、‘)’、和 ’ ’ 组成
s 表示一个有效的表达式
‘+’ 不能用作一元运算(例如, “+1” 和 “+(2 + 3)” 无效)
‘-’ 可以用作一元运算(即 “-1” 和 “-(2 + 3)” 是有效的)
输入中不存在两个连续的操作符
每个数字和运行的计算将适合于一个有符号的 32位 整数
class Solution:
def calculate(self, s: str) -> int:
#o(n)
presign=1
num=0
res=0
#o(n)
stack=[]
for c in s:
if c>='0' and c<='9':
num=num*10+ord(c)-ord('0')
elif c=="+":#更新,初始
res += presign*num
presign,num=1,0
elif c=='-':#更新,初始
res += presign*num
presign,num=-1,0
elif c=='(':#入栈,顺序,初始
stack.append(res)
stack.append(presign)
res,presign=0,1
elif c==')':#出栈,顺序,初始
res+=presign*num
res*=stack.pop()
res+=stack.pop()
num=0
return res+presign*num
lc 227:基本计算器二
https://leetcode.cn/problems/basic-calculator-ii/
提示:
1 <= s.length <= 3 * 105
s 由整数和算符 (‘+’, ‘-’, ‘*’, ‘/’) 组成,中间由一些空格隔开
s 表示一个 有效表达式
表达式中的所有整数都是非负整数,且在范围 [0, 231 - 1] 内
题目数据保证答案是一个 32-bit 整数
#先乘除,后累加
class Solution:
def calculate(self, s: str) -> int:
#o(n)
stack = []
#
presign='+'
num=0
for i in range(len(s)):
if s[i].isdigit():
num=num*10 + ord(s[i])-ord('0')
if i==len(s)-1 or not s[i].isdigit() and s[i]!=' ' :#例如"3+2*2":最后一个元素依然需要处理(而不是在最后简单的加减)
#if i==len(s)-1 or s[i] in '+-*/':
if presign=="+":stack.append(num)
elif presign=='-':stack.append(-num)
elif presign=='*':stack.append(num*stack.pop())
else:stack.append(int(stack.pop()/num))
#
num=0#初始
presign=s[i]#更新
#
return sum(stack)
lc 946【剑指 31】:验证栈序列
https://leetcode.cn/problems/validate-stack-sequences/
提示:
1 <= pushed.length <= 1000
0 <= pushed[i] <= 1000
pushed 的所有元素 互不相同
popped.length == pushed.length
popped 是 pushed 的一个排列
class Solution:
def validateStackSequences(self, pushed: List[int], popped: List[int]) -> bool:
stack=[]
i=0
for num in pushed:
stack.append(num)
while stack and stack[-1]==popped[i]: #key:while,否则无法一直比较,i不再更新
stack.pop()
i+=1
return i==len(popped)
单调栈
'''
#找出数组中右边第一个比我小的元素
'''
def findRightSmall(self, nums: List[int]) -> List[int]:
#o(n)
res=[-1]*len(nums)
#o(2n):单增栈
stack=list()
for i in range(len(nums)):
while stack and nums[i]<nums[stack[-1]]:#右第一小
res[stack[-1]]=i
stack.pop()
stack.append(i) #元素
return res
'''
#找出数组中右边第一个比我大的元素
'''
def findRightLarger(self, nums: List[int]) -> List[int]:
#o(n)
res=[-1]*len(nums)
stack=list()
#o(2n):单减栈
for i in range(len(nums)):
while stack and nums[i]>nums[stack[-1]]:#右第一大
res[stack[-1]]=i
stack.pop()
stack.append(i)
return res
'''
#找出数组中左边离我最近比我小的元素
'''
def findLeftSmall(self, nums: List[int]) -> List[int]:
res=[-1]*len(nums)
stack=[]
#单增栈(右->左)
for i in range(len(nums)-1,-1,-1):
while stack and nums[i]<nums[stack[-1]]:#左第一小
res[stack[-1]]=i
stack.pop()
stack.append(i)
return res
'''
#找出数组中左边离我最近比我大的元素
'''
def findLeftLager(self, nums: List[int]) -> List[int]:
res=[-1]*len(nums)
stack=[]
#单减栈(右->左)
for i in range(len(nums)-1,-1,-1):
while stack and nums[i]>nums[stack[-1]]:#左第一大
res[stack[-1]]=i
stack.pop()
stack.append(i)
return res
lc 739【剑指 038】【top100】:每日温度
https://leetcode.cn/problems/daily-temperatures/
提示:
1 <= temperatures.length <= 10^5
30 <= temperatures[i] <= 100
class Solution:
def dailyTemperatures(self, temperatures: List[int]) -> List[int]:
n=len(temperatures)
if n==1:return 0
#单减栈
stack=[]
res=[0]*n
for i in range(n):
while stack and temperatures[i]>temperatures[stack[-1]]:#右第一大
res[stack[-1]]=i-stack[-1]
stack.pop()
stack.append(i)
return res
lc 42【top100】:接雨水
https://leetcode.cn/problems/trapping-rain-water/
提示:
n == height.length
1 <= n <= 2 * 10^4
0 <= height[i] <= 10^5
#暴力
class Solution:
def trap(self, height: List[int]) -> int:
n = len(height)
if n <= 2: return 0
#o(n):预计算最大值
leftmax=[0]*n
leftmax[0]=height[0]
rightmax=[0]*n
rightmax[n-1]=height[n-1]
for i in range(1,n):
leftmax[i]=max(leftmax[i-1],height[i])
for i in range(n-2,-1,-1):
rightmax[i]=max(rightmax[i+1],height[i])
#o(n)
res=0
for i in range(1,n-1):
max_height=min(leftmax[i],rightmax[i])
if max_height>height[i]:
res+=max_height-height[i]
return res
#双指针(最优)
class Solution:
def trap(self, height: List[int]) -> int:
n = len(height)
if n <= 2: return 0
#o(1):变量代替数组
left,right=0,n-1
leftmax=rightmax=0
#o(n):
res=0
while left<right:
leftmax=max(leftmax,height[left])
rightmax=max(rightmax,height[right])
if height[left]<height[right]:#key:低洼处存水
res+=leftmax-height[left]
left+=1
else:
res+=rightmax-height[right]
right-=1
return res
#单调栈
class Solution:
def trap(self, height: List[int]) -> int:
n = len(height)
if n <= 2: return 0
#o(n):
stack=[]
res=0
for i in range(n):
while stack and height[i]>height[stack[-1]]:
low=stack.pop()
if not stack:break
left=stack[-1]
#
weight=i-left-1
heigt=min(height[i],height[left])-height[low]
res+=weight*heigt
stack.append(i)
#
return res
lc 84【剑指 039】:柱状图中最大的矩
https://leetcode.cn/problems/largest-rectangle-in-histogram/
提示:
1 <= heights.length <=10^5
0 <= heights[i] <= 10^4
class Solution:
def largestRectangleArea(self, heights: List[int]) -> int:
#单调栈一次遍历(融合为第二种方案)实现枚举高
n=len(heights)
#o(n),o(n)
left=[-1]*n
right=[n]*n
stack=[]
for i in range(n):
while stack and heights[stack[-1]]>=heights[i]:#直至找到左第一小
right[stack.pop()]=i #右第一小
left[i]=stack[-1] if stack else -1
stack.append(i)
#
res=0
for mid in range(0,n):
res=max(res,heights[mid]*(right[mid]-left[mid]-1))
return res
'''
left=[-1]*n
stack1=[]
for i in range(1,n):
while stack1 and heights[stack1[-1]]>=heights[i]:#直至找到左第一小
stack1.pop()
left[i]=stack1[-1]
stack1.append(i)
right=[8]*n
stack2=[]
for i in range(n-2,-1,-1):
while stack1 and heights[i]<=heights[stack1[-1]]:#直至找到右第一小
stack1.pop()
left[i]=stack1[-1]
stack1.append(i)
######################################################################
res1=[8]*n
stack3=[]
for i in range(1,n):
while stack3 and heights[stack3[-1]]>heights[i]:#右第一小
res1[stack3.pop()]=i
stack3.append(i)
res2=[-1]*n
stack4=[]
for i in range(n-2,-1,-1):
while stack4 and heights[i]<heights[stack4[-1]]:#左第一小
res2[stack4.pop()]=i
stack4.append(i)
'''
lc 85 【剑指 040】【top100】:最大矩形
https://leetcode.cn/problems/maximal-rectangle/
提示:
rows == matrix.length
cols == matrix[0].length
1 <= row, cols <= 200
matrix[i][j] 为 ‘0’ 或 ‘1’
'''
#计算左边连续为1的个数->问题变为求解每列的柱状图最大矩形面积
'''
class Solution:
def maximalRectangle(self, matrix: List[List[str]]) -> int:
#连续1
m=len(matrix)
if m==0: return 0
n=len(matrix[0])
constone=[[0]*n for _ in range(m)]
for i in range(m):
for j in range(n):
if matrix[i][j]=='1':
constone[i][j]=1 if j==0 else constone[i][j-1]+1
#o(m),o(m*n)最大面积(每一列)
res=0 #key:位置
for j in range(n):
leftmin=[-1]*m
rightmin=[m]*m
stack=[]
for i in range(m):
while stack and constone[i][j]<=constone[stack[-1]][j]:
rightmin[stack.pop()]=i
leftmin[i]=stack[-1] if stack else -1
stack.append(i)
#
#res=0:如果放在这里,计算下一行会被置零
for mid in range(m):
res=max(res,constone[mid][j]*(rightmin[mid]-leftmin[mid]-1))
return res
lc 321:拼接最大数
https://leetcode.cn/problems/create-maximum-number/
说明: 请尽可能地优化你算法的时间和空间复杂度。
要求从同一个数组中取出的数字保持其在原数组中的相对顺序。
class Solution:
def maxNumber(self, nums1: List[int], nums2: List[int], k: int) -> List[int]:
#
m,n=len(nums1),len(nums2)
max_Number=[0]*k
start,end=max(0,k-n),min(m,k)
#
for i in range(start,end+1):
number1=self.getMaxSubsequence(nums1,i)
number2=self.getMaxSubsequence(nums2,k-i)
curr_num=self.merge(number1,number2)
if self.compare(curr_num,0,max_Number,0)>0:
max_Number=curr_num
return max_Number
def getMaxSubsequence(self, nums, k):
n=len(nums)
stack=[]
remain=n-k #栈中先得有k个
for i in range(n):
while stack and nums[i]>stack[-1] and remain>0:
stack.pop()
remain-=1 #pop出的元素需要nums剩下的元素满足
if len(stack)<k:
stack.append(nums[i])
else:
remain-=1 #例如422-11-3,2—>3的时候,remain==1 not 3
return stack
def merge(self, num1, num2):
x, y = len(num1), len(num2)
if x == 0:return num2
if y == 0:return num1
merged=[0]*(x+y)
i=j=0
for k in range(x+y):
if self.compare(num1,i,num2,j)>0:
merged[k]=num1[i]
i+=1
else:
merged[k]=num2[j]
j+=1
return merged
# merged = list()
# i1 = i2 = 0
# for _ in range(x+y):
# if self.compare(num1, i1, num2, i2) > 0:
# merged.append(num1[i1])
# i1 += 1
# else:
# merged.append(num2[i2])
# i2 += 1
# return merged
#相对位
def compare(self,num1, i1, num2, i2):
l1,l2=len(num1),len(num2)
while i1<l1 and i2<l2:
diff=num1[i1]-num2[i2]
if diff!=0:return diff #相同,则比较后一位
i1+=1
i2+=1
return (l1-i1)-(l2-i2) #若一直相同,则看最长序列
lc 456:132 模式
https://leetcode.cn/problems/132-pattern/
提示:
n == nums.length
1 <= n <= 2 * 10^5
-10^9 <= nums[i] <= 10^9
#方案一:暴力(超时)
class Solution:
def find132pattern(self, nums: List[int]) -> bool:
n=len(nums)
if n<3:return False
mini=nums[0] #单变量维护最小值
#o(1),o(n^2)
for j in range(1,n):
#o(n):线性查找
for k in range(j+1,n):
if mini<nums[k]<nums[j]:return True
mini=min(mini,nums[j])
return False
#方案二:红黑树o(logn)-二叉搜索树
class Solution:
def find132pattern(self, nums: List[int]) -> bool:
n=len(nums)
if n<3:return False
#入二叉查找树
from sortedcontainers import SortedList
rightlist=SortedList(nums[2:]) #k=2,3...
#
mini=nums[0]
#o(1),o(n^2)
for j in range(1,n-1):
if nums[j]>mini:
k=rightlist.bisect_right(mini)#对应比mini大的元素索引
if k<len(rightlist) and rightlist[k]<nums[j]:return True
#更新mini和k
mini=min(mini,nums[j])
rightlist.remove(nums[j+1])
return False
#方案三:单调栈
class Solution:
def find132pattern(self, nums: List[int]) -> bool:
n=len(nums)
if n<3:return False
#前缀最小值数组
prefixmin=[0]*n
prefixmin[0]=nums[0]
for i in range(1,n):
prefixmin[i]=min(prefixmin[i-1],nums[i])
#o(n),o(n)
stack=[]
stack.append(nums[n-1])#初始化栈,因为j=n-2开始
for j in range(n-2,0,-1):
if nums[j]>prefixmin[j]:
while stack and prefixmin[j]>=stack[-1]:
stack.pop()
if stack and stack[-1]<nums[j]:
return True
stack.append(nums[j])#潜在k
return False
#方案四:单调栈及空间优化(不需要维护前缀最小值数组)
class Solution:
def find132pattern(self, nums: List[int]) -> bool:
n=len(nums)
if n<3:return False
#
maxk=float('-inf')
stack=[]
stack.append(nums[n-1])
for i_j in range(n-2,-1,-1):
#i
if nums[i_j]<maxk:return True
#j
while stack and nums[i_j]>stack[-1]:#直至找到比nums[j]小的最大nums[k]
maxk=stack.pop()#例如:[0,6,3,2,1,4]
if nums[i_j]>maxk:stack.append(nums[i_j])
return False
lc 151 【剑指 58-1】:翻转字符串里的单词
https://leetcode.cn/problems/reverse-words-in-a-string/
提示:
1 <= s.length <= 104
s 包含英文大小写字母、数字和空格 ’ ’
s 中 至少存在一个 单词
进阶:如果字符串在你使用的编程语言中是一种可变数据类型,请尝试使用 O(1) 额外空间复杂度的 原地 解法。
#方案一:使用内置 API
class Solution:
def reverseWords1(self, s: str) -> str:
return " ".join(reversed(s.split()))
#方案二:不使用API
class Solution:
def reverseWords(self, s: str) -> str:
s1=self.trim_space(s) #消空格
self.reverse(s1,0,len(s1)-1) #整体反
self.reverse_eachWords(s1) #单词反
return ''.join(s1)
def trim_space(self,s):
left,right=0,len(s)-1
while left<=right and s[left]==' ':left+=1
while left<=right and s[right]==' ':right-=1
#
out=[]
while left<=right:
if s[left]!=' ':
out.append(s[left])
elif out[-1]!=' ':#key:acc______bbc
out.append(' ')
left+=1
return out
def reverse(self,s,left,right):
while left < right:
s[left], s[right] = s[right], s[left]
left, right = left + 1, right - 1
def reverse_eachWords(self,s):
left=0
while left<len(s):
if s[left]!=' ':
#
right=left
while right+1<len(s) and s[right+1]!=' ':
right+=1
self.reverse(s,left,right)
#
left=right+2
else:
left+=1
#方案三:双端队列
class Solution:
def reverseWords(self, s: str) -> str:
#消空格
left,right=0,len(s)-1
while left<=right and s[left]==' ':left+=1
while left<=right and s[right]==' ':right-=1
#o(n),o(n)
singleWord=[]
deque=collections.deque()
while left<=right:
if s[left]!=' ':
singleWord.append(s[left])
elif singleWord:#key:acc____bcc
deque.appendleft(''.join(singleWord))
singleWord=[]
left+=1
deque.appendleft(''.join(singleWord))#key:因为最后一个字符不为空,不会执行elif
return ' '.join(deque)