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

Posted 常某某的好奇心

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了日常系列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以上是关于日常系列LeetCode《10·栈和队列篇》的主要内容,如果未能解决你的问题,请参考以下文章

日常系列LeetCode《11·堆和优先队列篇》

日常系列LeetCode《2·一维数组篇》

日常系列LeetCode《9·哈希查找篇》

日常系列LeetCode《12·滑动窗口篇》

日常系列LeetCode《4·字符串篇》

日常系列LeetCode《13·综合应用1篇》