leetcode分类刷题:队列(Queue)(二、优先队列解决TopK简单问题)

2023-11-07

1、优先队列好像一般都叫,以大顶堆为例,顶部第一个元素最大,底部最后一个元素最小,自顶向底是递减的(更准确的说是非递增的),对外只能访问顶部第一个元素(对应索引为0)和底部最后一个元素(对应索引为-1)在Python中,heapq默认维护小顶堆,构造大顶堆时需要在入堆时添加相反数
2、本次博客总结下用优先队列解决TopK简单问题,比如数组中第K大或小的元素、单个序列里第K大或小的距离、根据元素的出现频率排序的问题

215. 数组中的第K个最大元素

1、TopK简单问题的经典题目:求第K大,直接想到用大顶堆,把数组中所有元素入堆,返回堆中第k个元素作为结果
2、该题也可以用小顶堆,始终维护堆的元素总数为k,那么堆顶元素即为第k个最大元素——这种解法在实际中更合适,好像有一种TopK 大用小顶堆,TopK 小用大顶堆反着来的意思

from typing import List
import heapq
'''
215. 数组中的第K个最大元素
给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。
示例 1:
    输入: [3,2,1,5,6,4], k = 2
    输出: 5
题眼:Top K
思路1、大顶堆:返回堆中第k个元素作为结果
思路2、小顶堆:始终维护堆的元素总数为k,那么堆顶元素即为第k个最大元素
'''


class Solution:
    def findKthLargest(self, nums: List[int], k: int) -> int:
        # # 思路1、大顶堆:返回堆中第k个元素作为结果
        # que = []
        # for n in nums:
        #     heapq.heappush(que, -n)  # 添加相反数:因为python默认维护小顶堆
        # for _ in range(k - 1):
        #     heapq.heappop(que)
        # return -que[0]

        # 思路2、小顶堆:始终维护堆的元素总数为k,那么堆顶元素即为第k个最大元素
        que = []
        for n in nums:
            heapq.heappush(que, n)
            if len(que) > k:
                heapq.heappop(que)
        return que[0]


if __name__ == "__main__":
    obj = Solution()
    while True:
        try:
            in_line = input().strip().split('=')
            nums = []
            for n in in_line[0].split('[')[1].split(']')[0].split(','):
                nums.append(int(n))
            k = int(in_line[1].strip())
            print(obj.findKthLargest(nums, k))
        except EOFError:
            break

414. 第三大的数

1、TopK简单问题的经典题目:是“215. 数组中的第K个最大元素”的特例,K取3,同时注意这个题有两个细节:一是要把元素去重(使用集合),二是去重后元素总数不足3个时,返回最大值
2、按照上一个题的经验:TopK 大用小顶堆,TopK 小用大顶堆反着来,这道题直接用小顶堆,并始终维护堆的元素总数不超过k

from typing import List
import heapq
'''
414. 第三大的数
给你一个非空数组,返回此数组中 第三大的数 。如果不存在,则返回数组中最大的数。
示例 1:
    输入:[2, 2, 3, 1]
    输出:1
    解释:注意,要求返回第三大的数,是指在所有不同数字中排第三大的数。
    此例中存在两个值为 2 的数,它们都排第二。在所有不同数字中排第三大的数为 1 。
题眼:Top K
思路:“215. 数组中的第K个最大元素”的扩展,需要先将元素去重复
'''


class Solution:
    def thirdMax(self, nums: List[int]) -> int:
        nums = set(nums)  # 去重
        # 小顶堆,维持元素总数为3
        que = []
        for n in nums:
            heapq.heappush(que, n)
            if len(que) > 3:
                heapq.heappop(que)
        if len(que) < 3:
            return que[-1]
        else:
            return que[0]


if __name__ == "__main__":
    obj = Solution()
    while True:
        try:
            in_line = input().strip()[1: -1]
            nums = [int(i) for i in in_line.split(',')]
            print(obj.thirdMax(nums))
        except EOFError:
            break

703. 数据流中的第 K 大元素

1、TopK简单问题的经典题目:是“215. 数组中的第K个最大元素”的扩展,数组会不断的添加新元素进来,并要求添加元素时返回此时新数组的第 K 大元素
2、按照“215. 数组中的第K个最大元素”的经验:TopK 大用小顶堆,TopK 小用大顶堆反着来,这道题直接用小顶堆,并始终维护堆的元素总数不超过K,这样添加新元素时,进行一次入堆和一次出堆就搞定了,用大顶堆就会比较麻烦了

from typing import List
import heapq
'''
703. 数据流中的第 K 大元素
设计一个找到数据流中第 k 大元素的类(class)。注意是排序后的第 k 大元素,不是第 k 个不同的元素。
请实现 KthLargest 类:
KthLargest(int k, int[] nums) 使用整数 k 和整数流 nums 初始化对象。
int add(int val) 将 val 插入数据流 nums 后,返回当前数据流中第 k 大的元素。
示例 1:
    输入:
    ["KthLargest", "add", "add", "add", "add", "add"]
    [[3, [4, 5, 8, 2]], [3], [5], [10], [9], [4]]
    输出:
    [null, 4, 5, 5, 8, 8]
    解释:
    KthLargest kthLargest = new KthLargest(3, [4, 5, 8, 2]);
    kthLargest.add(3);   // return 4
    kthLargest.add(5);   // return 5
    kthLargest.add(10);  // return 5
    kthLargest.add(9);   // return 8
    kthLargest.add(4);   // return 8
题眼:Top K(“215. 数组中的第K个最大元素”的扩展)
思路、小顶堆:始终维护堆的元素总数为k,那么堆顶元素即为第k个频率的元素
'''


class KthLargest:
    def __init__(self, k: int, nums: List[int]):
        self.que = []
        self.k = k
        # 建立小顶堆维护,并保持总的元素数量为k,那么堆顶即为第k个最大的数
        for n in nums:
            heapq.heappush(self.que, n)
            if len(self.que) > k:
                heapq.heappop(self.que)

    def add(self, val: int) -> int:
        heapq.heappush(self.que, val)  # 为了防止队列内元素总数不足k个,因此把元素先加进去
        if len(self.que) > self.k:
            heapq.heappop(self.que)
        return self.que[0]


if __name__ == "__main__":
    obj = KthLargest(3, [4, 5, 8, 2])
    print(obj.add(3))
    print(obj.add(5))
    print(obj.add(10))
    print(obj.add(9))
    print(obj.add(4))

973. 最接近原点的 K 个点

1、TopK简单问题的经典题目:是“215. 数组中的第K个最大元素”的扩展,只要简单计算下距离就转换为第K小元素了
2、按照“215. 数组中的第K个最大元素”的经验:TopK 大用小顶堆,TopK 小用大顶堆反着来,这道题直接用大顶堆,并始终维护堆的元素总数不超过K,注意用小顶堆在Python中是添加相反数

from typing import List
import heapq
'''
973. 最接近原点的 K 个点
给定一个数组 points ,其中 points[i] = [xi, yi] 表示 X-Y 平面上的一个点,并且是一个整数 k ,返回离原点 (0,0) 最近的 k 个点。
这里,平面上两点之间的距离是 欧几里德距离( √(x1 - x2)2 + (y1 - y2)2 )。
你可以按 任何顺序 返回答案。除了点坐标的顺序之外,答案 确保 是 唯一 的。
示例 1:
    输入:points = [[1,3],[-2,2]], k = 1
    输出:[[-2,2]]
    解释: 
    (1, 3) 和原点之间的距离为 sqrt(10),
    (-2, 2) 和原点之间的距离为 sqrt(8),
    由于 sqrt(8) < sqrt(10),(-2, 2) 离原点更近。
    我们只需要距离原点最近的 K = 1 个点,所以答案就是 [[-2,2]]。
题眼:Top K(“215. 数组中的第K个最大元素”的扩展)
思路、题目是要求k个最小值,大顶堆:始终维护堆的元素总数为k,那么堆内元素即为k个最小值
'''


class Solution:
    def kClosest(self, points: List[List[int]], k: int) -> List[List[int]]:
        # k个最小值,大顶堆:维持元素总数为k
        result = []
        que = []
        for point in points:
            x, y = point[0], point[1]
            d = x * x + y * y
            heapq.heappush(que, (-d, [x, y]))  # 添加相反数:因为python默认维护小顶堆
            if len(que) > k:
                heapq.heappop(que)
        for _ in range(k):
            result.append(heapq.heappop(que)[1])
        return result


if __name__ == "__main__":
    obj = Solution()
    while True:
        try:
            in_line = input().strip().split('=')
            points = []
            for row in in_line[1][2: -5].split(']')[: -1]:
                points.append([int(n) for n in row.split('[')[1].split(',')])
            k = int(in_line[2].strip())
            print(obj.kClosest(points, k))
        except EOFError:
            break

347. 前 K 个高频元素

1、TopK简单问题的经典题目:是“215. 数组中的第K个最大元素”的扩展,需要先统计元素出现频率的哈希表,然后就是Top K问题的模板了
2、按照“215. 数组中的第K个最大元素”的经验:TopK 大用小顶堆,TopK 小用大顶堆反着来,这道题直接用小顶堆,并始终维护堆的元素总数不超过K,注意入堆的形式为(频率,元素)的元组

from typing import List
import heapq
'''
347. 前 K 个高频元素
给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。
示例 1:
    输入: nums = [1,1,1,2,2,3], k = 2
    输出: [1,2]
题眼:Top K(“215. 数组中的第K个最大元素”的扩展)
思路1、大顶堆:返回堆中第k个频率的元素作为结果
思路2、小顶堆:始终维护堆的元素总数为k,那么堆顶元素即为第k个频率的元素
'''


class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        # # 思路一、大顶堆:返回堆中第k个频率的元素作为结果
        # # 1、统计数组元素频率
        # hashTable = {}
        # for n in nums:
        #     if n not in hashTable:
        #         hashTable[n] = 1
        #     else:
        #         hashTable[n] += 1
        # # 2、构建大顶堆
        # que = []
        # for key in hashTable:
        #     heapq.heappush(que, (-hashTable[key], key))  # 添加相反数:因为python默认维护小顶堆
        # # 3、输出大顶堆的前k个元素
        # result = []
        # for i in range(k):
        #     result.append(heapq.heappop(que)[1])
        # return result

        # # 思路二、小顶堆:始终维护堆的元素总数为k,那么堆顶元素即为第k个频率的元素
        # 1、统计数组元素频率
        hashTable = {}
        for n in nums:
            if n not in hashTable:
                hashTable[n] = 1
            else:
                hashTable[n] += 1
        # 2、构建小顶堆,并维持其元素数量不多于k
        que = []
        for key in hashTable:
            heapq.heappush(que, (hashTable[key], key))
            if len(que) > k:
                heapq.heappop(que)
        # 3、输出大顶堆的k个元素,逆序放入结果数组
        result = [0] * k
        for i in range(k - 1, -1, -1):
            result[i] = heapq.heappop(que)[1]
        return result


if __name__ == "__main__":
    obj = Solution()
    while True:
        try:
            in_line = input().strip().split('=')
            nums = [int(i) for i in in_line[1].split('[')[1].split(']')[0].split(',')]
            k = int(in_line[2].strip())
            print(obj.topKFrequent(nums, k))
        except EOFError:
            break

451. 根据字符出现频率排序

1、TopK简单问题的经典题目:是“215. 数组中的第K个最大元素”的扩展,同样需要先统计元素出现频率的哈希表,
2、这道题是要对所有的字符出现频率进行排序,因此和“215. 数组中的第K个最大元素”的经验:TopK 大用小顶堆,TopK 小用大顶堆不那么一致了,直接用大顶堆,将所有(频率,元素)的元组入堆,然后持续出堆返回结果就可以了

import heapq
'''
451. 根据字符出现频率排序
给定一个字符串 s ,根据字符出现的 频率 对其进行 降序排序 。一个字符出现的 频率 是它出现在字符串中的次数。
返回 已排序的字符串。如果有多个答案,返回其中任何一个。
示例 1:
    输入: s = "tree"
    输出: "eert"
    解释: 'e'出现两次,'r'和't'都只出现一次。
    因此'e'必须出现在'r'和't'之前。此外,"eetr"也是一个有效的答案。
题眼:Top K
思路:这道题是要对所有的字符出现频率进行排序,因此和“215. 数组中的第K个最大元素”的经验:TopK 大用小顶堆,TopK 小用大顶堆不那么一致了,直接用大顶堆,
将所有(频率,元素)的元组入堆,然后持续出堆返回结果就可以了
'''


class Solution:
    def frequencySort(self, s: str) -> str:
        # 1、统计词频
        hashTable = {}
        for ch in s:
            if ch not in hashTable:
                hashTable[ch] = 1
            else:
                hashTable[ch] += 1
        # 2、建立大顶堆
        que = []
        for k in hashTable:
            heapq.heappush(que, (-hashTable[k], k))
        # 3、建立结果字符串
        result = ''
        while len(que) > 0:
            pair = heapq.heappop(que)
            result += pair[1] * (-pair[0])
        return result


if __name__ == "__main__":
    obj = Solution()
    while True:
        try:
            s = input().strip().split('=')[1].strip()[1: -1]
            print(obj.frequencySort(s))
        except EOFError:
            break

692. 前K个高频单词

1、TopK简单问题的经典题目:是“215. 数组中的第K个最大元素”的扩展,同样需要先统计元素出现频率的哈希表,额外增加了对词频相同的元素的排序要求:字典次序小的排前面,因此需要重写<,即__lt__()函数
2、按照“215. 数组中的第K个最大元素”的经验:TopK 大用小顶堆,TopK 小用大顶堆反着来,这道题直接用小顶堆,并始终维护堆的元素总数不超过K,注意入堆的形式为(频率,元素)的元组
3、由于维护的是小顶堆,那么频率值小的就要排在堆顶,频率值一样时让字典次序大的排在堆顶,这样保留下来的K个元素就满足题意要求的频率值大,字典次序小,最后把这些元素出堆逆序存放到结果数组中

from typing import List
import heapq
'''
692. 前K个高频单词
给定一个单词列表 words 和一个整数 k ,返回前 k 个出现次数最多的单词。
返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率, 按字典顺序 排序。
示例 1:
    输入: words = ["i", "love", "leetcode", "i", "love", "coding"], k = 2
    输出: ["i", "love"]
    解析: "i" 和 "love" 为出现次数最多的两个单词,均为2次。
    注意,按字母顺序 "i" 在 "love" 之前。
题眼:Top K(“215. 数组中的第K个最大元素”的扩展)
思路:题目是要求k个最大值,小顶堆:始终维护堆的元素总数为k,那么堆内元素即为k个最大值
难点:这道题对答案的唯一性进行了限制,因此要重写小于号!
'''


class Solution:
    def topKFrequent(self, words: List[str], k: int) -> List[str]:
        class Comparer:  # 重写小于号:出现次数少的在堆顶、次数一样字典序大的在堆顶
            def __init__(self, val, key):
                self.val = val
                self.key = key

            def __lt__(self, other):
                if self.val != other.val:
                    return self.val < other.val
                return self.key > other.key

        # 1、统计词频
        hashTable = {}
        for n in words:
            if n not in hashTable:
                hashTable[n] = 1
            else:
                hashTable[n] += 1
        # 2、建立优先队列:维护元素总数为k
        que = []
        for key in hashTable:
            heapq.heappush(que, Comparer(hashTable[key], key))
            if len(que) > k:
                heapq.heappop(que)
        # 3、输出结果
        result = ['0'] * k
        for i in range(k - 1, -1, -1):
            result[i] = heapq.heappop(que).key
        return result
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

leetcode分类刷题:队列(Queue)(二、优先队列解决TopK简单问题) 的相关文章

随机推荐

  • vrep笔记

    这些天主要对vrep做了一些探索 一些笔记如下 1 urdf机器人模型文件的导入 点击plugins urdf importing即可 2 动力学模型的配置 将此处改成零 大意是以方框的正中心为质心 否则很容易抖 模型导入后坐标系都会被这个
  • NLP预训练模型系列-BERT

    NLP预训练模型系列文章目录 1 BERT 2 GPT 3 GPT 2 4 GPT 3 5 RoBERTa 6 BART 7 XLNet 目录 NLP预训练模型系列文章目录 前言 从BERT开始 1 Abstract 2 Introduct
  • 基于Django的员工管理系统1

    主题 员工管理系统 1 新建项目 2 创建app python manage py startapp app01 点击 run manage py Task 然后输入startapp app01 注册app 3 设计表结构 models p
  • IT技术岗位面试怎么介绍自己的项目经验?

    泽林又一批学员即将毕业 需要为面试做一些准备 都说面试7份靠能力 3份靠技能 而开始时的介绍项目又是技能中的重中之重 决定一次面试的成败 那么面试时如果介绍自己的项目呢 泽林教育为你们梳理了一份详细的项目经验介绍 预测面试官提问 先规划好答
  • android-smart-image-view源码分析

    public class BitmapImage implements SmartImage 定义Bitmap对象 private Bitmap bitmap 构造方法 public BitmapImage Bitmap bitmap th
  • 字面值。。

    1概念 不能改变的量 2 分类 基本类型 整型 short int 没有短整型字面值 int 100 d long int 100L ld long long int 100LL lld unsigned short int 没有短整型字面
  • git 在不同服务器主机上同步 git 仓库

    git 在不同服务器主机上同步 git 仓库 参考链接 https opentechguides com how to article git 177 git sync repos html 1 在本地的一个文件夹中执行 git clone
  • js实现AES加密

    安装第三方加密包 npm i crypto js 加密代码 let str 需要加密的字符串 let keyStr 密钥 let ivStr iv偏移量 const key CryptoJS enc Utf8 parse keyStr 十六
  • WGS84坐标系下大地坐标转换为空间直角坐标

    大地坐标表示方法 BLH 空间直角坐标表示方法 XYZ 进行地图投影的一般操作步骤为先将BLH转换为XYZ 然后将XYZ通过三参数或者7参数的办法转换为xyz 涉及到两个椭球体以及坐标系之间的转换 本文主要讨论BLH转换为XYZ的办法 通过
  • 线性代数的本质(二)——线性变换与矩阵

    文章目录 线性变换与矩阵 线性变换与二阶方阵 常见的线性变换 复合变换与矩阵乘法 矩阵的定义 列空间与基 矩阵的秩 逆变换与逆矩阵 线性变换与矩阵 线性变换与二阶方阵 本节从二维平面出发学习线性代数 通常选用平面坐标系 O x y Oxy
  • Java中jdbc的框架

    使用框架可以简化代码 提高开发效率 所以了解和掌握一些框架也是必须的 下面简单介绍几个jdbc框架 1 jdbcTemplate Spring提供 2 commons dbutils Apache提供 小巧的jdbc轻量级封装的工具包 主要
  • 【YARN】(1)-- 整体架构、RM、NM、AM等基础组件快速理解

    一 Yarn的功能和整体架构 Apache Hadoop YARN Yet Another Resource Negotiator 另一种资源协调者 是一种新的 Hadoop 资源管理器 它是一个通用资源管理系统和调度平台 可为上层应用提供
  • 什么是自动化测试?如何开展自动化测试你需要知道这些点

    目录 前言 什么是自动化测 分层的自动化测试 我为什么要做自动化测试 什么项目适合做自动化测试 选择什么工具进行自动化测试 selenium 用前须知 selenium IDE selenium Grid selenium RC selen
  • 怎样用苹果手机播放html文件夹,无需转格式 如何用iPhone轻松爽看各种片

    iPhone 5问世后 瞬间就成为了大家追随的最热门产品之一 无论是最具创新还是最热门 每一款产品推出后总是会存在遗憾的 iPhone 5同样不例外 在大家眼中它可能有这样或那样的问题 但是在我看来 自带视频播放器仅支持指定苹果标准视频 不
  • uni-app根据经纬度逆解析详细地址

    uni app中的getLocation 方法可以获取到用户当前的地理位置 经纬度 速度 但是返回参数中的address在app中才会显示 小程序中不会显示 所以我们需要进行逆解析其地址 解析出它的地址信息 1 首先要在腾讯位置服务中 控制
  • 第三方登陆--接入谷歌和FaceBook

    一 第三方登陆流程 一 用户点击登录 前端会调用第三方的SDK 获取到对应的数据 一般会有token userId 二 前端拿到这些信息之后 回调自己后端服务端的接口 进行token校验 主要目的是后端得防止他人使用恶意手段 别的平台 或者
  • Ubuntu下安装LLVM/Clang

    关于LLVM和Clang 参考原文 https blog csdn net SiberiaBear article details 103111028 LLVM 起初的作者是 Chris Lattner 博硕期间研究关于编译器优化的东西 其
  • 区块链:盗版者的噩梦?

    传统版权保护是用文本或数据库来进行处理的 用纸张文本处理有诸多不便之处 如记录搜寻 纸质保存 文件遗失等 而使用普通数据库 虽然查询速度加快 但其中的数据是可以被篡改的 因此很难被视为有效的电子证据 数字资产难以确权 同时再加上如今极度便利
  • LLVM passes: MergeFunctions Pass

    目录 What is MergeFunctions Pass 概述 FnTree和Deferred 基本流程 相同函数搜索 函数哈希值比较 函数哈希值的计算 函数哈希值比较的使用 函数结构比较 FunctionNodeCmp 函数比较方法
  • leetcode分类刷题:队列(Queue)(二、优先队列解决TopK简单问题)

    1 优先队列好像一般都叫堆 以大顶堆为例 顶部第一个元素最大 底部最后一个元素最小 自顶向底是递减的 更准确的说是非递增的 对外只能访问顶部第一个元素 对应索引为0 和底部最后一个元素 对应索引为 1 在Python中 heapq默认维护小