代码随想录二刷 day8 栈与队列篇 括号匹配问题 单调队列O(n) 优先级队列O(nlogk)

二刷复习

栈和队列篇



栈是一种先入后出的数据结构,以底层容器完成其所有的工作,提供push 和 pop 等接口,不提供走访功能,也不提供迭代器(iterator)不能遍历

栈是一种容器适配器,它可以用list实现也可以是deque实现

队列是先入先出的数据结构

在python中,from collections import deque,deque()是可以遍历的

from collections import deque

q = deque()
q.append('a')
q.append('b')
q.append('c')
for x in q:
    print(x)

python中的deque()加元素是从右边加的,右边是队尾,popleft()是把元素从队头也就是队首pop出去;
记住右进左出

232.用栈实现队列(两个栈倒一下)

用栈实现队列

这个题的考点在于考队列和栈的区别
队列是先入先出,栈是先入后出;用栈模拟队列的关键点在于pop()函数怎么设计,就是说两个栈全空才是队列空,如果stack_out不空就直接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
        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:
        peek = self.pop()
        self.stack_out.append(peek)
        return peek


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


225.用队列实现栈(把前n-1个元素加到队尾)

用队列实现栈
也是考察栈和队列的基本性质,也是主要实现在于pop()函数
思路在于把队列的前n-1个元素给pop()出来再加回到队尾

python中的deque()加元素是从右边加的,右边是队尾,popleft()是把元素从队头也就是队首pop出去;
记住右进左出

class MyStack:

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

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

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

    def top(self) -> int:
        return self.queue[-1]

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

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

删除字符串相邻重复项
这个没什么好说的

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

有效的括号

有效的括号
水平还是很菜,这道题看着案例想出把左括号加进栈,遇到对应的右括号等于栈顶的时候pop是很简单的思路;但是这道题不容易想的情况是右括号多或者右括号不匹配的情况;而代码中的else:就隐含了这两种情况

class Solution:
    def isValid(self, s: str) -> bool:
        a = {')':'(', '}':'{', ']':'[' }
        
        stack = []
        for ch in s:
            if ch not in a.keys():
                stack.append(ch)
            elif stack and a[ch] == stack[-1]:
                stack.pop()
            else: return False    #这个else很重要,隐含了两种情况,右括号多或者右括号不匹配
        return not stack

后缀表达式求值(逆波兰表达式求值)

后缀表达式求值
这个题的思路是很简单的,遇到运算符的话从栈里掏出来两个数运算,但是要注意顺序因为栈的后入先出;

不要用isdigit(),有负数

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

239. 滑动窗口最大值(单调队列 O(n))

滑动窗口最大值
这个题的话需要用单调队列,单调队列的时间的复杂度是O(n)的;但是如果用滑窗加max()函数,时间复杂度是O(nlogk)会TLE

然后这个单调队列的话,是递减的,队首是大的,队尾是小的,关键还是在于pop()函数,如果val > 队尾就一直pop(),把右边的小的都pop出去然后把大的放在左边;到时候直接返回队首就能get最大值

from collections import deque
class myqueue:
    def __init__(self):
        self.queue = deque()

    def pop(self, val):
        if self.queue and val == self.queue[0]: #要pop不一定真移除,要判断一下是否是val == 队首元素;因为有可能已经不在了
            self.queue.popleft()

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

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

class Solution:
    def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
        queue = myqueue()
        res = []
        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

补充:deque

相比于list实现的队列,deque实现拥有更低的时间和空间复杂度。
list实现出队(pop)和插入(insert)时的空间复杂度大约为O(n),
deque在出队(pop)和入队(append)时的时间复杂度是O(1)。

这是因为:列表实现是基于数组的。pop(0)从列表中删除第一个项,它需要左移len(lst) - 1个项来填补空白。

deque()实现使用双向链表。因此无论deque有多大,deque.popleft()都需要一个常量的操作数。

求前k个高频元素(优先级队列O(nlogn))

这个题是用优先级队列的,优先级队列就是用大顶堆或者小顶堆实现的
建堆O(n),调整O(logn);

主要学一下heapq 之前的博客

1.建堆, 注意建的堆是小顶堆
heap = []
for item in [2, 3, 1, 4]:
    heapq.heappush(heap, item)
print heap     # 输出 [1, 3, 2, 4]

或者
heap = [2,3,1,4]
heapq.heapify(heap)
print heap       # 输出 [1, 3, 2, 4]

2.heapq.heappush()
heapq.heappush(heap名字,元素)
heapq.heappush(heap, -10)
print heap                     # 输出 [-10, 1, 2, 4, 3]

3.heapq.heappop(heap名字)
print heapq.heappop(heap)           # 输出 -10
print heap                          # 输出 [1, 3, 2, 4]

4.heapq.nlargest(n, heap的名字)  # 前n大的数
  heapq.nsmallest(n, heap的名字) #前n小的数
l = [2, 3, 1, 4]
print heapq.nlargest(2, l)

1.要统计元素出现频率
2.对频率建立小顶堆
3.找出前K个高频元素

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        num_dict = collections.Counter(nums)
        heap = []
        for key, freq in num_dict.items():
            heapq.heappush(heap, (freq, key))
            if len(heap) > k: heapq.heappop(heap)
        res = [0] * k
        for i in range(k):
            res[i] = heapq.heappop(heap)[1]
        return res[::-1]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值