刷题day3--栈和队列

理论知识

在这里插入图片描述

	- 先进后出

队列

	- 先进先出

1、栈实现队列

使用栈实现队列的下列操作:

push(x) – 将一个元素放入队列的尾部。
pop() – 从队列首部移除元素。
peek() – 返回队列首部的元素。
empty() – 返回队列是否为空。

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、队列实现栈

push(x) – 元素 x 入栈
pop() – 移除栈顶元素
top() – 获取栈顶元素
empty() – 返回栈是否为空

from collections import deque
class MyStack:

    def __init__(self):
        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
        for i in range(len(self.queue_in) -1):
            self.queue_out.append(self.queue_in.popleft())
        self.queue_out,self.queue_in = self.queue_in, self.queue_out
        return self.queue_out.popleft()


    def top(self) -> int:
        if self.empty():
            return  None
        for i in range(len(self.queue_in) - 1):
            self.queue_out.append(self.queue_in.popleft())
        self.queue_in, self.queue_out = self.queue_out, self.queue_in
        temp = self.queue_out.popleft()
        self.queue_in.append(temp)
        return temp


    def empty(self) -> bool:
        return not (self.queue_in or self.queue_out)

一个队列实现

def __init__(self):
        self.que = deque()

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

    def pop(self) -> int:
        if self.empty():
            return None
        for i in range(len(self.que)-1):
            self.que.append(self.que.popleft())
        return self.que.popleft()

    def top(self) -> int:
        # 写法一:
        # if self.empty():
        #     return None
        # return self.que[-1]

        # 写法二:
        if self.empty():
            return None
        for i in range(len(self.que)-1):
            self.que.append(self.que.popleft())
        temp = self.que.popleft()
        self.que.append(temp)
        return temp

    def empty(self) -> bool:
        return not self.que

3、有效的括号

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

有效字符串需满足:

左括号必须用相同类型的右括号闭合。
左括号必须以正确的顺序闭合。
注意空字符串可被认为是有效字符串。
示例 1:

输入: “()”
输出: true
示例 2:

输入: “()[]{}”
输出: true

思路----对称匹配类问题
1、栈解决
2、有先后顺序

class Solution:
    def isValid(self, s: str) -> bool:
        stack = []
        for i in s:
            if i == "(":
                stack.append(")")
            elif i == "{":
                stack.append("}")
            elif i == "[":
                stack.append("]")
            elif not stack or stack[-1] != i:
                return False
            else:
                stack.pop()
        if not stack:
            return True
        else:
            return False

4、删除字符串职工所有相邻重复项

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

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

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

示例:

输入:“abbaca”
输出:“ca”
解释:例如,在 “abbaca” 中,我们可以删除 “bb” 由于两字母相邻且相同,这是此时唯一可以执行删除操作的重复项。之后我们得到字符串 “aaca”,其中又只有 “aa” 可以执行重复项删除操作,所以最后的字符串为 “ca”。

class Solution:
    def removeDuplicates(self, s: str) -> str:
        '''
        理解:题目遇见一个相邻重复要重新从前一个开始判断
        栈:
            压进去,遇见相同的pop出去
        '''
        res = list()
        for item in s:
            if res and res[-1] == item:
                res.pop()
            else:
                res.append(item)
        return "".join(res)  # 字符串拼接

5、逆波兰表达式求值

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

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

说明:

整数除法只保留整数部分。 给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。

示例 1:

输入: [“2”, “1”, “+”, “3”, " * "]
输出: 9
解释: 该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9

class Solution:
    def evalRPN(self, tokens: List[str]) -> int:
        '''
        理解题目:
             只要遇见+ - * /的中的任何一个就结合前面两个数据进行计算
             包括计算之后的结果,计算的结果也算一个数字,
        重点---递归就是栈

        '''
        stack = []
        res = 0
        for token in tokens:
            if token not in {"+","-","*","/"}:
                stack.append(int(token))
            else:
                op2 = stack.pop()
                op1 = stack.pop()
                if token == "+":
                    res = op2 + op1
                elif token == "-":
                    res = op1- op2
                elif token == "*":
                    res = op1 * op2
                else:
                    res =int(op1/op2)
                stack.append(res)
        return stack.pop()

6、滑动窗口最大值

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

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


from collections import deque
class MyQueue:
    def __init__(self):
        self.queue = deque()
    def pop(self,value):
        if self.queue and value ==  self.queue[0]:
            self.queue.popleft()
    def push(self, value):
        while self.queue and value > self.queue[-1]:
            self.queue.pop()
        self.queue.append(value)
    def front(self):
        return self.queue[0]
class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        '''
        题目理解:
            1、窗口大小固定为K
            2、窗口内找最大
            3、每次前后都移动一个
        思路:
            1、双指针模拟窗口找最值
            2、窗口张的像队列--考虑
        问题:
            怎么在窗口里面找最大---暴利循环--浪费
            保证队列中有序
                单调队列
                pop: p出去的值等于队列出口的元素,队列p出元素
                push:放进来的数据大于入口的数据,队列入口的数据pop出去,只有push的值小于等于才停

        '''
        due = MyQueue()
        # 最终输出的结果存放处
        result = []
        # 先把窗口大小搞出来
        for i in range(k):
            due.push(nums[i])
        # 最开始的窗口最最大值
        result.append(due.front())
        # 窗口最前方的开始往前走
        
        for i in range(k , len(nums)):
            # 移动后面的就要漏一个走
            due.pop(nums[i-k])
            # 移动进一步数据放进去
            due.push(nums[i])
            result.append(due.front())
        return result 

7、前K个高频元素

给定一个非空的整数数组,返回其中出现频率前 k 高的元素。

示例 1:

输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]

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
class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
    	#字典统计次数
       dict_time = defaultdict(int)
        for n in nums:
            dict_time[n] += 1
        # 更改字典,key为出现次数,value为相应的数字的集合
        # 反转统计的次数,排Key,找Key的前K个
        index_dict = defaultdict(list)
        for ki in dict_time:
            index_dict[dict_time[ki]].append(ki)

        key = list(index_dict.keys())
        key.sort()
        res  =[]
        time = 0
        while key and time != k:
            res += index_dict[key[-1]]
            time += len(index_dict[key[-1]])
            key.pop()
        return res[:k]

总结


  • - 匹配问题
    - 比较比对
  • 队列
    - 张的像队列的
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值