首页 > 其他分享 >栈和队列——4.前k个高频元素

栈和队列——4.前k个高频元素

时间:2024-08-04 11:26:59浏览次数:13  
标签:map 排序 nums 队列 元素 key 小顶 高频

力扣题目链接

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

示例:

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

题干很简单,就是对数组中的元素进行频次计算,找到频次最多的前k和元素。那么首先就要统计元素出现的频率,然后对其进行排序,返回前k个值。

统计频率很简单,用个map映射一下,出现一次次数加一就行了,那么如何进行排序呢?《代码随想录》中介绍了一种利用堆的方法。什么是堆呢,堆是一棵完全二叉树,树中每个结点的值都不小于(或不大于)其左右孩子的值。 如果父亲结点是大于等于左右孩子就是大顶堆,小于等于左右孩子就是小顶堆。因为小顶堆可以每次将最小的元素弹出,所以这里选择小顶堆。关于二叉树的内容在下一章会介绍到。

根据示例中[1,1,1,2,2,3],‘1’有3次,‘2’有2次,‘3’有1次,那我小顶堆长什么样子呢?

这里为元素值,而不是元素的次数,由于是小顶堆,所以这三个中3是对应次数最小的,即以1——2——3的顺序排列,因为k=2,那我在堆中再进行一次弹出,弹出了3,留下了[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

首先,导入堆的第三方库heapq。第一部分,计算元素频率。定义一个空的map,map_[nums[i]] = map_.get(nums[i], 0) + 1是简化了代码的过程,完整是如果nums[i]不在map中,那就将其加入map,代码map_.get(nums[i], 0);如果nums[i]在map中,那就将其次数加1.

第二部分,定义一个小顶堆,开始扫描map,利用heapq.heappush自动开始建立小顶堆,该函数的()中,第一个为顶堆表示pri_que,第二个为(freq, key)代表先根据freq来排序的。如果两个元素的频率相同,则会根据key值来决定它们在堆中的顺序。

然后,只保留前k个数,如果堆的长度大于k,就heappop弹出最小的值。

定义一个大小为k的空数组,因为堆每次弹出的是最小的,但要求的结果数组是由大到小排列的,所以我们要从数组最后开始填充for i in range(k-1, -1, -1),从k-1索引(即数组最后)到能取到的0,每次以-1的步长。 result[i] = heapq.heappop(pri_que)[1],result[i]等于每次堆中弹出的值的[1]索引,即上面(freq, key)中的key。最后完成结果数组。

那我不用这个小顶堆行么,map不是自带排序么,我直接排序一下,找到前k个值不行么?也可以,利用map的排序,完整代码如下:

from typing import List

class Solution:
    def topKFrequent(self, nums: List[int], k: int) -> List[int]:
        # 统计每个元素出现的频率
        map_ = {}  # 元素: 出现次数
        for num in nums:
            map_[num] = map_.get(num, 0) + 1
        
        # 按照出现频率排序,得到频率最高的前 k 个元素
        sorted_items = sorted(map_.items(), key=lambda x: x[1], reverse=True)
        
        # 取出前 k 个元素的键
        result = [item[0] for item in sorted_items[:k]]
        
        return result

统计元素频率是一样的。sorted_items = sorted(map_.items(), key=lambda x: x[1], reverse=True)中利用sort进行排序,定义选择排序的函数key=lambda x: x[1],以x[1]即(key,freq)中的freq为对象进行排序。

result = [item[0] for item in sorted_items[:k]]代码是简化后的,但也就是定义空数组,然后将排序后数组中的[:k]部分取出来的item中的第一个数item[0](即key)取出来填入result。简化的代码写不出来就安安心心的拆分开来写完整一点。

从代码简洁度和理解来讲,map直接排序好像更简单一点,那这两种方法的区别在哪里呢?在于k的大小,对于小k的情况,小顶堆方法可能更高效,而对于较大k,排序方法可能更简单和直接。

标签:map,排序,nums,队列,元素,key,小顶,高频
From: https://blog.csdn.net/plutomty/article/details/140903036

相关文章

  • 【面试题解答】一个有序数组 nums ,原地删除重复出现的元素
    面试题解答仅供学习文章目录面试题解答题目一、python代码1.1代码1.2示例用法1.2.1示例11.2.2示例2二、讲解2.1初始化2.2遍历2.3返回题目要解决这个问题,可以使用双指针方法进行原地修改,以确保每个元素最多出现两次。一、python代码1.1代码defr......
  • 代码随想录算法训练营day03|203.移除链表元素,707.设计链表,206.反转链表
    203.移除链表元素题目链接:https://leetcode.cn/problems/remove-linked-list-elements/description/我的代码(分头节点和中间节点两种情况操作):/***Definitionforsingly-linkedlist.*structListNode{*intval;*ListNode*next;*ListNode():val......
  • 数据结构 -- 栈和队列
    数据结构--栈和队列1.栈1.1栈的概念及结构1.2栈的实现2.队列2.1队列的概念及结构2.2队列的实现3.栈和队列面试题4.概念选择题1.栈1.1栈的概念及结构栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端称为栈顶,另一端......
  • [消息队列]延迟消息
    死信队列是什么死信,在官网中对应的单词为“DeadLetter”,可以看出翻译确实非常的简单粗暴。那么死信是个什么东西呢?“死信”是RabbitMQ中的一种消息机制,当你在消费消息时,如果队列里的消息出现以下情况:消息被否定确认,使用channel.basicNack或channel.basicReject,并且此时re......
  • Rabbitmq中的死信队列
    背景        RabbitMQ死信队列俗称,备胎队列;消息中间件因为某种原因拒收该消息后,可以转移到死信队列中存放,死信队列也可以有交换机和路由key等。原理        死信队列和普通队列区别不是很大        普通与死信队列都有自己独立的交换机和路由ke......
  • javascript学习 - DOM 元素获取、属性修改
    什么是WebAPIWebAPI是指网页服务器或者网页浏览器的应用程序接口。简单来讲,就是我们在编写JavaScript代码时,可以通过WebAPI来操作HTML网页和浏览器。WebAPI又可以分为两类:DOM(文档对象模型)BOM(浏览器对象模型)DOM(DocumentObjectModel),即文档对象模型,主要用......
  • Pandas的30个高频函数使用介绍
    Pandas是Python中用于数据分析的一个强大的库,它提供了许多功能丰富的函数。本文介绍其中高频使用的30个函数。read_csv():从CSV文件中读取数据并创建DataFrame对象。importpandasaspddf=pd.read_csv('data.csv')read_excel():从Excel文件中读取数据并创......
  • 实现吸顶效果,一个页面多个元素吸顶效果
    前言新业务开发用到了吸顶效果而且是一个页面滚动到不同的位置不同的元素进行吸顶叠加。我是基于uniapp去写的,原理思路都一样代码部分下面的代码我写了两种方法都是一样的一个是通过js控制变量添加元素一个是直接通过css样式进行控制 <!--上半部总览位置--> <view......
  • JAVA中实现队列和栈(Deque接口和ArrayDeque类)
    用什么来实现队列和栈首先JAVA中有一个Queue接口,用来实现队列。Deque其实就是双端队列,代表两端都可进可出的队列。ArrayDeque就是用数组来实现这个双端队列。(Deque由于是接口,只可以用于声明对象,但是没办法实例化,实例化还是要使用ArrayDeque类)这时可能就会产生疑惑,队列有了,......
  • 【leetcode232:用栈实现队列】
    leetcode232:用栈实现队列题目:请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):实现MyQueue类:voidpush(intx)将元素x推到队列的末尾intpop()从队列的开头移除并返回元素intpeek()返回队列开头的元素booleanemp......