【日常系列】LeetCode《10·栈和队列篇》

数据规模->时间复杂度

<=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)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值