栈与队列 stack & queue

栈和队列的相互实现

232 用栈实现队列

  • easy
  • 题目描述

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):

实现 MyQueue 类:

void push(int x) 将元素 x 推到队列的末尾
int pop() 从队列的开头移除并返回元素
int peek() 返回队列开头的元素
boolean empty() 如果队列为空,返回 true ;否则,返回 false

  • 解题思路

分别设置输入和输出栈(用数组来模拟),元素先加入输入栈,当需要pop元素时,从输出栈中直接pop,如果输出栈没有元素,从输入栈中转移元素(因为元素经历了先加入输入栈再从末尾pop,加入到输出栈的过程,所以当输出栈pop时,它的顺序就是队列的顺序)

class MyQueue:

    def __init__(self):
        # 用数组来模拟两个栈
        self.stack_in = []
        self.stack_out = []


    def push(self, x: int) -> None:
        self.stack_in.append(x)


    def pop(self) -> int:
        if self.empty():
            return None
        if self.stack_out:
            return self.stack_out.pop()
        else:
            # 一次性把stack_in中的元素转移到stack_out中来
            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:
        # 首先用pop()函数移除元素,然后再加回来
        temp = self.pop()
        self.stack_out.append(temp)
        return temp


    def empty(self) -> bool:
        return not (self.stack_in or self.stack_out)



# Your MyQueue object will be instantiated and called as such:
# obj = MyQueue()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.peek()
# param_4 = obj.empty()

225 用队列实现栈

  • easy
  • 题目描述

请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。

实现 MyStack 类:

void push(int x) 将元素 x 压入栈顶。
int pop() 移除并返回栈顶元素。
int top() 返回栈顶元素。
boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。

  • 解题思路

第二个队列仅用于复制第一个队列的部分元素,因为队列先进先出的特性,导致队列A送入队列B后,元素顺序不变

class MyStack:

    def __init__(self):
        # 第二个队列用于保存第一个pop出来的元素
        self.queue_in = deque()
        self.queue_out = deque()


    def push(self, x: int) -> None:
        self.queue_in.append(x)


    def pop(self) -> int:
        if self.empty():
            return None
        # 先把除栈顶外其余元素送入out中
        for i in range(len(self.queue_in) - 1):
            self.queue_out.append(self.queue_in.popleft())
        ans = self.queue_in.popleft()
        # 这里互换以后,queue_out是空,queue_in是pop后的结果
        self.queue_in, self.queue_out = self.queue_out, self.queue_in
        return ans


    def top(self) -> int:
        if self.empty():
            return None
        return self.queue_in[-1]


    def empty(self) -> bool:
        if len(self.queue_in) == 0:
            return True
        return False




# Your MyStack object will be instantiated and called as such:
# obj = MyStack()
# obj.push(x)
# param_2 = obj.pop()
# param_3 = obj.top()
# param_4 = obj.empty()

栈的实际应用

20 有效的括号

  • easy
  • 题目描述

给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。

  • 解题思路

因为是字符串前后匹配的问题,自然想到用栈来实现,当匹配到对应的括号时,直接从栈中弹出匹配项(为此在入栈时直接加入左括号的对称符号-右括号,这样可以方便比较)

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 item != stack[-1]:
                return False
            else:
                stack.pop()
        return True if not stack else False

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

  • easy
  • 题目描述

给出由小写字母组成的字符串 S,重复项删除操作会选择两个相邻且相同的字母,并删除它们。

在 S 上反复执行重复项删除操作,直到无法继续删除。

在完成所有重复项删除操作后返回最终的字符串。答案保证唯一。

  • 解题思路

思路和上面的括号匹配一样,遇到相同的直接弹出对应项(需要注意的是,如果一个字符出现偶数次,那一定可以全部删除,如果是奇数次,则会剩余一个)

class Solution:
    def removeDuplicates(self, s: str) -> str:
        # 栈入栈和出栈相互抵消
        stack = []
        for item in s:
            if not stack:
                stack.append(item)
            elif item == stack[-1]:
                stack.pop()
            else:
                stack.append(item)
        return "".join(stack)

150 逆波兰表达式求值

  • medium
  • 题目描述

根据 逆波兰表示法,求表达式的值。

有效的算符包括 +、-、*、/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。

注意 两个整数之间的除法只保留整数部分。

可以保证给定的逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。

  • 解题思路

//和int(/)还是不一样的,//是向下取较小值,而int是直接取整数,如果运算数中有负数,这两者有很大的区别

逆波兰表达式就是运算数优先于运算符出现,也就是根节点是运算符,子节点是运算数

class Solution:
    def evalRPN(self, tokens: List[str]) -> int:
        # 每次遇到运算符都要出栈2个数字
        stack = []
        operator = ["+", "-", "*", "/"]
        for item in tokens:
            # 数字
            if item not in operator:
                number = int(item)
                stack.append(number)
            # 运算符
            else:
                # 弹出2个操作数
                op2 = stack.pop()
                op1 = stack.pop()
                if item == "+":
                    stack.append(op1+op2)
                elif item == "-":
                    stack.append(op1-op2)
                elif item == "*":
                    stack.append(op1*op2)
                else:
                    stack.append(int(op1/op2))
        print(stack)
        print(6//-132)
        print(int(6/-132))
        return stack[-1]

单调队列

239 滑动窗口最大值

  • hard
  • 题目描述

给你一个整数数组 nums,有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。

返回 滑动窗口中的最大值 。

  • 解题思路

需要注意的是,因为滑动窗口是动态向右走的,所以每个滑动窗口都有一个最大值

为此设计一个单调队列的数据结构,用单调队列代替记录滑动窗口中的单调递减数列,每次把最新元素加入窗口中并更新该单调队列(更新的原则就是比当前值小,直接加入,比当前值大,用该数替换当前值,直到满足队列的单调递减性即可)

队列没有必要维护窗口里的所有元素,只需要维护有可能成为窗口里最大值的元素就可以了,同时保证队列里的元素数值是由大到小的

# 单调队列,从大到小
class MyQueue:
    def __init__(self):
        self.queue = []
    
    def push(self, value):
        # 把小于当前value的值全部删掉(这里用while是因为可能当前value比窗口中所有值都要大)
        while self.queue and value > self.queue[-1]:
            self.queue.pop()
        self.queue.append(value)

    def front(self):
        return self.queue[0]

    def pop(self, value):
        if self.queue and value == self.queue[0]:
            self.queue.pop(0)

    

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        queue = MyQueue()
        res = []
        # 把前k个元素压入栈中
        for i in range(k):
            queue.push(nums[i])
        res.append(queue.front())

        # 计算剩余滑动窗口的最大值
        for i in range(k, len(nums)):
            # 构成新的窗口
            queue.pop(nums[i-k])
            queue.push(nums[i])
            res.append(queue.front())
        return res

优先级队列(堆)

347 前k个高频元素

  • medium
  • 题目描述

给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。

  • 解题思路

如果用大顶堆,那么每次都把最大的元素弹出去了,无法保留前k个高频元素;所以需要用小顶堆,每次把最小的元素弹出去,最后小顶堆里剩下的k个元素就是最大的k个高频元素

最后剩下的小顶堆,每次弹出堆中的最小值,所以保存结果的时候需要逆序保存

本质上是一个带权重的优先级队列问题

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        # 堆排序,对带权值的元素按照权值进行排序
        fre_dict = collections.defaultdict(int)
        # 元素和频率的对应关系
        for i in nums:
            fre_dict[i] += 1
        # 优先级队列
        pri_que = []
        # 小顶堆
        for key, value in fre_dict.items():
            # 按照频率的大小加入堆中
            heapq.heappush(pri_que, (value, key))
            # 每次弹出最小值
            if len(pri_que) > k:
                heapq.heappop(pri_que)
        
        res = [0 for i in range(k)]
        # 找出前k个高频元素
        for i in range(k-1, -1, -1):
            res[i] = heapq.heappop(pri_que)[1]
        return res
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值