栈与队列相关题

1. 用栈实现队列

题目:

在这里插入图片描述
思路:

python中使用列表来实现栈的操作

list.append(x) # 代表进栈
list.pop()  # 代表出栈

用栈实现队列,需要采用俩个栈,一个输入栈一个输出栈

实现过程:

  1. 在push数据的时候,只要数据放进输入栈就好,但在pop的时候,操作就复杂一些,输出栈如果为空,就把进栈数据全部导入进来(注意是全部导入),再从出栈弹出数据,如果输出栈不为空,则直接从出栈弹出数据就可以了。
  2. 最后如何判断队列为空呢?如果进栈和出栈都为空的话,说明模拟的队列为空了。

代码:

class MyQueue:

    def __init__(self):
        """
        in主要负责push,out主要负责pop
        初始化俩个输入输出栈
        """
        self.stack_in = []
        self.stack_out = []

    def push(self, x: int) -> None:
        """
        有新元素进来,就往in里面push
        """
        self.stack_in.append(x)

    def pop(self) -> int:
        """
        Removes the element from in front of queue and returns that element.
        """
        if self.empty():
            return None
        
        if self.stack_out:
            return self.stack_out.pop()
        else:
            for i in range(len(self.stack_in)):
                self.stack_out.append(self.stack_in.pop())
            return self.stack_out.pop()

    def peek(self) -> int:
        """
        Get the front element.
        """
        ans = self.pop()
        self.stack_out.append(ans)
        return ans

    def empty(self) -> bool:
        """
        只要in或者out有元素,说明队列不为空
        """
        return not (self.stack_in or self.stack_out)

2. 有效的括号

题目:
在这里插入图片描述
思路:

括号匹配是使用栈解决的经典问题。

入栈技巧:在匹配左括号的时候,右括号先入栈,就只需要比较当前元素和栈顶相不相等就可以了

经过分析,括号匹配问题,一共有三种不匹配的情况:

  1. 第一种情况,字符串里左方向的括号多余了 ,所以不匹配。

  2. 第二种情况,括号没有多余,但是 括号的类型没有匹配上。

  3. 第三种情况,字符串里右方向的括号多余了,所以不匹配。

第一种情况:已经遍历完了字符串,但是栈不为空,说明有相应的左括号没有右括号来匹配,所以return false

第二种情况:遍历字符串匹配的过程中,发现栈里没有要匹配的字符。所以return false

第三种情况:遍历字符串匹配的过程中,栈已经为空了,没有匹配的字符了,说明右括号没有找到对应的左括号return false

最后,字符串遍历完后,并且栈为空,就说明全部匹配完成了。

代码:

# 方法一,仅使用栈,更省空间
class Solution:
    def isValid(self, s: str) -> bool:
        stack = []
        
        for item in s:
            if item == '(':
                stack.append(')')
            elif item == '[':
                stack.append(']')
            elif item == '{':
                stack.append('}')
            elif not stack or stack[-1] != item:
                return False
            else:
                stack.pop()
        
        return True if not stack else False
        

3. 删除字符串中的所有相邻重复项

题目:

在这里插入图片描述

思路:

使用栈解决该问题。匹配问题都是栈的强项。

代码:

class Solution:
    def removeDuplicates(self, s: str) -> str:
        stack = []
        for i in s:
            if not stack or i != stack[-1]:
                stack.append(i)
            else:
                stack.pop()
        return ''.join(stack)

4. 逆波兰表达式求值

题目:

在这里插入图片描述
思路:

用栈操作运算:遇到数字则入栈;遇到运算符则取出栈顶两个数字进行计算,并将结果压入栈中。

代码:

class Solution:
    def evalRPN(self, tokens: List[str]) -> int:
        stack = []
        for i in tokens:
            if i != '+' and i != '-' and i != '*' and i != '/':
                stack.append(int(i))
            else:
                num1 = stack.pop()
                num2 = stack.pop()
                if i == '+':
                    stack.append(num2 + num1)
                elif i == '-':
                    stack.append(num2 - num1)
                elif i == '*':
                    stack.append(num2 * num1)
                elif i == '/':
                    stack.append(int(num2 / num1))
        return stack.pop()

【注意】

# 注意俩者的区别
num1 = -6
num2 = 132
print(int(num1 / num2))   ---> 0  
print(num1 // num2)  ---> -1

5. 滑动窗口最大值

题目:

在这里插入图片描述
思路:

单调队列的经典题目。

单调队列

维护元素单调递减的队列就叫做单调队列,即单调递减或单调递增的队列。Python中没有直接支持单调队列,需要我们自己来实现一个单调队列。

如何实现单调队列呢?

pop 和 push操作要保持如下规则:

  1. pop(value):如果窗口移除的元素value等于单调队列的出口元素,那么队列弹出元素,否则不用任何操作
  2. push(value):如果push的元素value大于入口元素的数值,那么就将队列入口的元素弹出,直到push元素的数值小于等于队列入口元素的数值为止

保持如上规则,每次窗口移动的时候,只要问que.front()就可以返回当前窗口的最大值。

定义单调队列

from collections import deque


class MyQueue: #单调队列(从大到小
    def __init__(self):
        self.queue = deque() #这里需要使用deque实现单调队列,直接使用list会超时
    
    #每次弹出的时候,比较当前要弹出的数值是否等于队列出口元素的数值,如果相等则弹出。
    #同时pop之前判断队列当前是否为空。
    def pop(self, value):
        if self.queue and value == self.queue[0]:
            self.queue.popleft()#list.pop()时间复杂度为O(n),这里需要使用collections.deque()
            
    #如果push的数值大于入口元素的数值,那么就将队列后端的数值弹出,直到push的数值小于等于队列入口元素的数值为止。
    #这样就保持了队列里的数值是单调从大到小的了。
    def push(self, value):
        while self.queue and value > self.queue[-1]:
            self.queue.pop()
        self.queue.append(value)
        
    #查询当前队列里的最大值 直接返回队列前端也就是front就可以了。
    def front(self):
        return self.queue[0]

【注】

  1. 开头导入了Python标准库中的 deque 类,该类实现了双端队列数据结构,可以高效地从队列的两端执行添加和删除操作。
  2. 首先要明确的是,题解中单调队列里的pop和push接口,仅适用于本题。单调队列不是一成不变的,而是不同场景不同写法,总之要保证队列里单调递减或递增的原则,所以叫做单调队列

整体代码实现

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        que = MyQueue()
        result = []
        for i in range(k): #先将前k的元素放进队列
            que.push(nums[i])
        result.append(que.front()) #result 记录前k的元素的最大值
        for i in range(k, len(nums)):
            que.pop(nums[i - k]) #滑动窗口移除最前面元素
            que.push(nums[i]) #滑动窗口前加入最后面的元素
            result.append(que.front()) #记录对应的最大值
        return result

时间复杂度: O(n)
空间复杂度: O(k)

6. 前K个高频元素

题目:
在这里插入图片描述
思路:

这道题目主要涉及到如下三块内容:

  1. 要统计元素出现频率
  2. 对频率排序
  3. 找出前K个高频元素

对频率进行排序,我们可以使用一种 容器适配器就是优先级队列

什么是优先级队列?

优先级队列就是一个披着队列外衣的堆,因为优先级队列对外接口只是从队头取元素,从队尾添加元素,再无其他取元素的方式,看起来就是一个队列。

如何排序?

可以使用堆对元素进行排序

什么是堆?

堆是一棵完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子的值。 如果父亲结点是大于等于左右孩子就是大顶堆,小于等于左右孩子就是小顶堆。

该使用大顶堆还是小顶堆呢?

我们要用小顶堆,因为要统计最大前k个元素,将小顶堆里保持k个最大元素,然后每次将最小的元素弹出,最后小顶堆里积累的才是前k个最大元素。

代码实现

#时间复杂度: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

【注】

  1. heapq 是 Python 标准库中的一个模块,提供了堆队列算法的实现。堆(heap)是一种特殊的数据结构,它是一个数组,可以看作一个近似完全二叉树。在堆中,父节点的值总是小于或等于任何一个子节点的值。这种性质决定了堆的根节点是最小元素。heapq中默认是小顶堆。

  2. heapq 模块提供了一些用于操作堆的函数,主要是将一个列表(或其他可迭代对象)转换为堆、进行堆的插入、删除等操作。这个模块的目标是提供高效的堆队列操作,通常用于优先级队列等场景。

  3. heapq 模块中一些常用的函数:

    heapq.heapify(iterable): 将可迭代对象转换为堆数据结构。

    heapq.heappush(heap, ele): 将元素推入堆中。

    heapq.heappop(heap): 弹出并返回堆中的最小元素。

    heapq.heapreplace(heap, ele): 弹出并返回堆中的最小元素,然后推入一个新元素。

    heapq.nlargest(n, iterable, key=None): 返回可迭代对象中最大的 n 个元素。

    heapq.nsmallest(n, iterable, key=None): 返回可迭代对象中最小的 n 个元素。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值