leetcode算法训练十一天| 239. 滑动窗口最大值,347.前 K 个高频元素,栈与队列总结

239. 滑动窗口最大值

学习视频:单调队列正式登场!| LeetCode:239. 滑动窗口最大值_哔哩哔哩_bilibili

学习文档:代码随想录 (programmercarl.com)

学习时间:14:00-15:30

记录时间:15:41-16:00

状态:已听懂|可单独复写代码|需复习

1. 看到问题后的初始想法与看完随想录后的心得

        看到这道题目第一眼的时候我最先想到的是暴力求解,但是在评论中看到暴力求解会超时,遂作罢,直接看视频。看完视频后对于队列有了更新的认识。原来单调队列还可以用来维护滑动窗口中的最大值(单调队列设计的很巧妙)!下面我先来阐述一下单调队列的特性,然后再详细说下单挑队列的构造方法:

单调队列的特性:单调队列从左往右依次为降序排列。(与优先级队列仅仅在表现形式上相同,内核完全不一样)

单调队列的构造方式:单调队列的本质是双向队列,其具有3个方法,第一个是pop操作,具体表现为当我们需要pop的val与单调队列的第一个元素相同时(即滑动窗口向右移动的过程中,滑动窗口或单调队列中最大的数据需要被移出去时),我们把单调队列中第一个元素pop出去。第二个是push操作,如果我们要push的元素比单调队列中最后一个元素大,我们就把最后一个元素pop出去,一直重复到我们要push的元素比单调队列最后一个元素小为止(因为如果push进来的元素比单调队列中最后的元素大,那么说明此时窗口已经划到了我们要push进元素的位置,由于单调队列中最后的元素比当前push进来的更小,并且当前单调队列最后的元素的生命周期比push进来元素更短,说明队列最后的元素在其生命周期内永远也没机会做最大的数字了,因此我们直接将其pop出去。同时这个操作也保证了单调队列的单调性)第三个是getmax(),直接返回队列第一个元素即可。我们python使用deque这个双向队列(底层容器为双向链表来构造单调队列)

代码如下:

from collections import deque

class mydeque:
    def __init__(self):
        self.queue = deque()
        self.size = 0
    
    def pop(self, val):
        if self.size != 0 and val == self.queue[0]:
            self.queue.popleft()
            self.size -= 1
    
    def push(self, val):
        while self.size != 0 and val > self.queue[-1]:
            self.queue.pop()
            self.size -= 1
        self.queue.append(val)
        self.size += 1
    
    def getmax(self):
        max_num = self.queue[0]
        return max_num


class Solution(object):
    def maxSlidingWindow(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: List[int]
        """
        a = mydeque()
        result = []
        for i in range(k):
            a.push(nums[i])

        result.append(a.getmax())
        left = 0
        right = k
        length = len(nums)

        while right < length:
            a.pop(nums[left])
            a.push(nums[right])
            result.append(a.getmax())
            left += 1
            right += 1
        return result

347.前 K 个高频元素

学习视频:优先级队列正式登场!大顶堆、小顶堆该怎么用?| LeetCode:347.前 K 个高频元素_哔哩哔哩_bilibili

学习文档:代码随想录 (programmercarl.com)

学习时间:16:00-17:22

记录时间:17:23-17:35

状态:已听懂|可单独复写代码|需复习

1. 看到问题后的初始想法与看完随想录后的心得

        刚刚看到这题就想到了使用python中的sort函数对字典中的value从大到小进行排序,最后输出前k个最大值,不过这就使用了python sort的函数了(虽然等一下使用的方法也需要用到heapq,但至少我们知道heapq的实现原理),这道题也就失去了考察的意义。这道题目想让我们设计一个数据结构以对元素出现的频率进行排列。而一般的排列算法性能最好时间复杂度也得o(nlogn),而我们这里并不需要对所有n个元素进行排列,只需要一直对前k个大的元素排列即可。我们这里就使用小顶堆进行操作。小顶堆的特点是父节点小于左右子节点,因此根节点是最小的。我们把小顶堆的个数限制在k个,当有个新的元素插入后,如果小顶堆的个数已经超过k个了,我们就把根节点pop掉。在python中我们使用heapq来快速实现小顶堆(后面会有题目需要我们从底层实现小顶堆,故这里就不自己构造了)

import heapq

class Solution(object):
    def topKFrequent(self, nums, k):
        """
        :type nums: List[int]
        :type k: int
        :rtype: List[int]
        """
        k_frequency = {}
        min_heap = []
        for i in nums:
            k_frequency[i] = k_frequency.get(i, 0) + 1

        for (key, value) in k_frequency.items():
            if min_heap is None or len(min_heap) < k:
                heapq.heappush(min_heap, (value, key))
            else:
                heapq.heappush(min_heap, (value, key))
                heapq.heappop(min_heap)
        result = []
        for i in min_heap:
            result.append(i[1])
        return result

需要注意的是,python中的heapq是使用数组来实现树结构的,因此min_heap是数组。 

栈与队列总结

        栈和队列都是基于底层构造的数据结构,所以我们更需要了解栈与队列的底层结构。在python的collections中,栈与队列都是基于deque(双向链表)这个底层数据结构。因此我们可以知道栈和队列的内存地址是不连续的。而并不是所有队列都是基于双向链表的,比如优先级队列,其是基于小顶堆(小顶堆在python中是基于数组的)。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值