leetcode刷题——栈与队列

队列 vs 栈

  • 栈:从头进,从头出,只有头部一个进出口
  • 队列:从尾进,从头处,头和尾分别负责出和进。适用于配对问题。

20. 有效的括号
运用栈尾进头出的思想实现配对

  • 当我们遇到一个左括号时,我们会期望在后续的遍历中,有一个相同类型的右括号将其闭合。由于后遇到的左括号要先闭合,因此我们可以将这个左括号放入栈顶stack.append(item)。

  • 当我们遇到一个右括号时,我们需要将一个相同类型的左括号闭合。此时,我们可以取出栈顶的左括号stack[-1], 并判断它们是否是相同类型的括号if stack[-1]=item。

    • 如果不是相同的类型,返回 \text{False}False。
    • 或者栈中并没有左括号,那么字符串 ss 无效,返回 \text{False}False。
    • 如果是相同的类型,则删除此配对,并继续遍历

    # 方法一,仅使用栈,更省空间
        stack = []
        for item in s:
        # 左括号
            if item == '(':
                stack.append(')')
            elif item == '[':
                stack.append(']')
            elif item == '{':
                stack.append('}')
            # 右括号
            elif not stack or stack[-1] != item:    # 如果不是相同的类型,或者栈中并没有左括号,那么字符串 ss 无效,返回 \text{False}False。
                return False
            else:
                stack.pop()# 配对成功,删除此配对,继续遍历
        
        return True if not stack else False # 最后True应该是stack为空
        
# 方法二,使用字典
class Solution:
    def isValid(self, s: str) -> bool:
        stack = []
        mapping = {
            '(': ')',
            '[': ']',
            '{': '}'
        }
        for item in s:
            if item in mapping.keys():
                stack.append(mapping[item])
            elif not stack or stack[-1] != item:   # 匹配不成功或没有匹配项
                return False
            else:    # 匹配成功
                stack.pop()   
        return True if not stack else False

1047. 删除字符串中的所有相邻重复项
相邻重复运用队列思想,先进后出

  • 匹配成功(重复):删除重复元素stack.pop()
  • 匹配不成功(不重复):加入队列,继续遍历stack.append(s[i])
class Solution:
    def removeDuplicates(self, s: str) -> str:
        stack=[]
        stack.append(s[0])
        for i in range(1,len(s)):
            if stack and stack[-1]==s[i]:stack.pop()
            else:stack.append(s[i])
        return ''.join(stack)

150. 逆波兰表达式求值

  • 代码注意点:把字符串’+‘,‘2’,‘3’转化为数值计算:eval(f’{second_num} {item} {first_num}’)
class Solution:
    def evalRPN(self, tokens: List[str]) -> int:
        stack=[]
        for item in tokens:
            if item not in {'+','-','*','/'}:stack.append(item)  # 数字则入栈
            else:    # 符号则计算
                first_num, second_num = stack.pop(), stack.pop()  # 先出两个
                stack.append(int(eval(f'{second_num} {item} {first_num}')))   #再进一个计算结果
        return int(stack.pop())

347. 前 K 个高频元素
【法一】counter计数

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        from collections import Counter
        count_dict=sorted(Counter(nums).items(),key=(lambda x:x[1]),reverse=True)  # 根据次数来逆序排序,是列表中元素为元组的形式
        return [x[0] for x in count_dict[:k]]

【法二】heapq求前n个最大/最小值

  • low3 =heapq.nlargest(3, list)
  • top3 = heapq.nsmallest(3, list)
  • heapq.heappush(heaptree,item) # 向堆中加入元素
  • heapq.heapop(heaptree) # 从堆中弹出元素

可以用优先级队列的思想

时间复杂度:O(nlogk)
#空间复杂度:O(n)
import heapq
class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        #要统计元素出现频率
        map_ = {} #nums[i]:对应出现的次数
        for i in range(len(nums)):
            map_[nums[i]] = map_.get(nums[i], 0) + 1
        
        #对频率排序
        #定义一个小顶堆,大小为k
        pri_que = [] #小顶堆
        
        #用固定大小为k的小顶堆,扫面所有频率的数值
        for key, freq in map_.items():
            heapq.heappush(pri_que, (freq, key))
            if len(pri_que) > k: #如果堆的大小大于了K,则队列弹出,保证堆的大小一直为k
                heapq.heappop(pri_que)
        
        #找出前K个高频元素,因为小顶堆先弹出的是最小的,所以倒叙来输出到数组
        result = [0] * k
        for i in range(k-1, -1, -1):
            result[i] = heapq.heappop(pri_que)[1]
        return result

字符串解码
(太难了)
递归问题可以用栈来解决,从小问题剖析,先进先出
本题核心思路是在栈里面每次存储两个信息, (左括号前的字符串, 左括号前的数字), 比如abc3[def]:

  • 当遇到字母:记录到res
  • 当遇到数字:记录
  • 当遇到第一个左括号的时候,压入栈中的是(“abc”, 3), 然后遍历括号里面的字符串def
  • 当遇到右括号的时候, 从栈里面弹出一个元素(s1, n1), 得到新的字符串为s1+n1*“def”, 也就是abcdefdefdef。对于括号里面嵌套的情况也是同样处理方式。
    凡是遇到左括号就进行压栈处理,遇到右括号就弹出栈,栈中记录的元素很重要。

stack.pop() 表示读取最后一个值并删除
stack[-1] 表示读取最后一个值

class Solution:
    def decodeString(self, s: str) -> str:
        stack = []  # (str, int) 记录之前的字符串和括号外的上一个数字
        num = 0 # 辅助记录数字
        res = ""  # 实时记录当前可以提取出来的字符串
        for c in s:
            if c.isdigit():  # 是数字  1-
                num = num * 10 + int(c) # 针对两位数,在遍历中读取两位数
            elif c == "[":  # [ 2-
                stack.append((res, num))  # res指的是[之前的正确结果
                res, num = "", 0
            elif c == "]":  # ] 4-
                top = stack.pop()
                res += top[0] + res * top[1]
            else:  # 是字母 3-
                res += c
        return res
       

你可能感兴趣的:(算法,leetcode,算法,职场和发展)