代码随想录day10,day12:栈与队列

Day10,Day12:栈+队列速通

摆烂了。。。完全摆烂了,反思一下自己,总是想要把每一次都做得很好,导致压力先把自己搞摆烂了,现在开始转变一下心态重新开始,能跟一点是一点吧,完成比完美更重要

232.用栈实现队列

225. 用队列实现栈

基础操作,只是比较繁琐,就不贴上来了

20.有效的括号

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

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 每个右括号都有一个对应的相同类型的左括号。

这个题目要把握好如何使用栈,并且搞清楚不匹配的情况有哪些。

还有一些技巧,在匹配左括号的时候,右括号先入栈,就只需要比较当前元素和栈顶相不相等就可以了,比左括号先入栈代码实现要简单的多了!

class Solution(object):
    def isValid(self, s):
        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

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

栈的经典应用,跟前一个题目一样,都是消消乐型的题目。

而且只需要控制好什么时候入栈,然后需要放入元素时比较一下栈顶就ok

class Solution(object):
    def removeDuplicates(self, s):
        stack = []

        for item in s:
            if not stack:
                stack.append(item)
                continue
            if item == stack[-1]:
                stack.pop()
            else:
                stack.append(item)
        
        return "".join(stack)

150. 逆波兰表达式求值

给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。

请你计算该表达式。返回一个表示表达式值的整数。

实际上就是一个算数的栈表达,很简单,但编写代码要考虑的就比较多,我使用了python的eval()函数进行了字符串的算式运算,在本地跑的结果是正确的,但是放在力扣上跑就错误了,不知道为啥

class Solution(object):
    def evalRPN(self, tokens):
        stack = []

        for token in tokens:
            if token not in ["+","-","*","/"]:
                stack.append(token)
            else:
                op1 = stack.pop()
                op2 = stack.pop()
                r = int(eval(op2 + token + op1))
                stack.append(str(r))
        return int(stack[-1])

还是借鉴一下答案给的方法吧


op_map = {'+': add, '-': sub, '*': mul, '/': div}
stack.append(self.op_map[token](op1, op2))

还要注意的是 第一个op1出来的在运算符后面

239. 滑动窗口最大值

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

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

这里需要实现的一个数据结构是,希望可以像队列一样pop和push,同时可以调用一个方法来返回队内的最大值

如果使用暴力方法去查找队内最大值,那有点浪费

于是我们可以选择去维护一个队列,在这个队列中最大值永远在队首,队伍后面跟着的都是比队首元素小的(可能在某个时候成为队伍最大值的元素)

那么这样的一个队列,在push一个元素时,要把所有比这个元素小的元素都先从队尾排出;在pop一个元素时,只需要看这个元素是否是队头元素,如果相同就pop队头即可;这样之后,要找到这个队中最大的数字永远在队头,只要pop队头就好。

class MyQueue: 
    def __init__(self):
        self.queue = deque() #这里需要使用deque实现单调队列,直接使用list会超时
    
    def pop(self, value):
        if self.queue and value == self.queue[0]:
            self.queue.popleft()

    def push(self, value):
        while self.queue and self.queue[-1] < value:
            self.queue.pop()
        self.queue.append(value)

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

class Solution(object):
    def maxSlidingWindow(self, nums, k):
        que = MyQueue()
        result = []
        j = -k
        for i in range(len(nums)):
            if j < 0 :
                j += 1
                que.push(nums[i])
                continue
            else:
                result.append(que.front())
                que.push(nums[i])
                que.pop(nums[j])
                j += 1
        result.append(que.front())
        return result

347.前k个高频元素

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

前k个高频元素,最好的方法肯定是使用堆来进行动态筛选,实际上也可以称为优先级队列。每个元素进行插入堆的操作,每一次插入一个大小为k的堆用时为O(logk),所以时间复杂度为O(nlogk)。当堆的大小大于k时就挤出一个最小的值(堆顶的值),所以这个题目使用小根堆来解决。

第一次使用python来写堆操作,还是熟悉一下heapq模块

Python中heapq模块浅析_heapq.heappush-CSDN博客

现在有一个列表heap

heapq.heappush(heap,a)是往堆中添加新值,此时自动建立了小根堆

但heapq里面没有直接提供建立大根堆的方法,可以采取如下方法:每次push时给元素加一个负号(即取相反数),此时最小值变最大值,反之亦然,那么实际上的最大值就可以处于堆顶了,返回时再取负即可。

heapq.heapfy(heap)是以线性时间将一个列表转化为小根堆

heapq.heappop()

从堆中弹出并返回最小的值

 

import heapq 
class Solution(object):
    def topKFrequent(self, nums, k):
        map_ = {}
        for i in range(len(nums)):
            map_[nums[i]] = map_.get(nums[i], 0) + 1
        #这个很实用,可以用来快速计算频次

        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)
        
        result = [0] * k
        for i in range(k):
            result[i] = heapq.heappop(pri_que)[1]
        return result

栈和队列的总结

赶进度一天就结束了,来用自己的话总结一下

(1)栈与队列的理论基础

一道面试题:栈里面的元素在内存中是连续分布的么?

这个问题有两个陷阱:

  • 陷阱1:栈是容器适配器,底层容器使用不同的容器,导致栈内数据在内存中不一定是连续分布的。
  • 陷阱2:缺省情况下,默认底层容器是deque,那么deque在内存中的数据分布是什么样的呢? 答案是:不连续的,下文也会提到deque。

在python中,deque(双向队列)是collections模块常用类型,是栈和队列的一种广义实现,deque是"double-end queue"的简称;deque支持线程安全、有效内存地以近似O(1)的性能在deque的两端插入和删除元素,尽管list也支持相似的操作,但是它主要在固定长度操作上的优化,从而在pop(0)和insert(0,v)(会改变数据的位置和大小)上有O(n)的时间复杂度。

(2)栈的典型应用

括号匹配、字符串去重,逆波兰表达式,总结来说,都是“消消乐”问题

(3)队列的经典应用

滑动窗口最大值(单调队列):维护一个队列使得其中的元素输出总是单调的(?)可以说的不太严谨,感觉是一个自定义调整维护的队列,使其完成要求

求频率topk(堆):这里要注意,找最大topk是维护小根堆,因为需要不断地淘汰最小的那个元素。

感觉这两个题目实际上也不是完全的队列,“队列”的底层是deque双向队列,是可以两边拓展的,队列更像是一种思想,在实现过程中还需要多多思考

  • 10
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值