问题描述
给你一个整数数组 nums 和一个整数 k ,请你返回其中出现频率前 k 高的元素。你可以按任意顺序返回答案。
- 1 <= nums.length <= 10^5
- k 的取值范围是 [1, 数组中不相同的元素的个数]
- 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的
你所设计算法的时间复杂度必须优于 O(n log n) ,其中 n 是数组大小。
示例 1
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2
输入: nums = [1], k = 1
输出: [1]
问题分析:
1、参数:
vector& nums: 输入的整数数组。 int k: 需要返回的前k个高频元素的数量。 2、局部变量:
unordered_map<int, int> freqMap: 用于存储每个数字的频率。 vector<pair<int,int>> result: 存储哈希表中的键值对(数字及其频率)。 stringstream ss: 用于构建最终返回的字符串。 3、逻辑流程:
构建频率映射: 遍历输入数组 nums,使用 unordered_map 记录每个数字的频率。 存储映射到结果向量: 将 freqMap 中的每个键值对(数字及其频率)添加到 result 向量中。 排序: 使用 sort 函数对 result 向量进行排序,排序依据是元素的频率(降序)。 构建返回字符串: 遍历排序后的 result 向量的前 k 个元素,将它们转换为字符串并使用逗号分隔,存储在 stringstream 中。 返回结果: 将 stringstream 中的内容转换为字符串并返回。
二、解题思路
为了满足时间复杂度的要求,可以利用哈希表(字典)来统计每个元素在数组中的出现频率,然后通过构建小顶堆(或大顶堆)来找出出现频率前 k 高的元素。具体步骤如下:
-
统计元素出现频率:
- 使用哈希表(例如在 Python 中可以使用字典)来遍历数组
nums,将每个元素作为键,该元素在数组中出现的次数作为值,进行频率统计。例如,对于数组nums = [1,1,1,2,2,3],经过统计后哈希表可能为{1: 3, 2: 2, 3: 1}。
- 使用哈希表(例如在 Python 中可以使用字典)来遍历数组
-
构建小顶堆(或大顶堆) :
-
方法一:构建小顶堆
- 可以创建一个大小为
k的小顶堆。遍历哈希表中的每个元素及其频率,将元素频率对作为元组(例如(频率, 元素))压入小顶堆中。如果小顶堆的大小超过了k,则弹出堆顶元素(因为小顶堆的堆顶是当前堆中频率最小的元素对)。这样,在遍历完哈希表后,小顶堆中就保留了出现频率前k高的元素对应的频率和元素信息。 - 例如,对于哈希表
{1: 3, 2: 2, 3: 1},假设k = 2,当遍历到(3, 1)时,小顶堆为空,直接压入;当遍历到(2, 2)时,小顶堆未满,压入;当遍历到(1, 3)时,小顶堆已满,比较(1, 3)的频率1和小顶堆堆顶元素(假设此时堆顶为(2, 2))的频率2,因为1 < 2,所以弹出堆顶元素(2, 2),然后压入(1, 3)。最终小顶堆中保留的是出现频率前k高的元素对应的信息。
- 可以创建一个大小为
-
方法二:构建大顶堆
- 同样创建一个大小为
k的堆,但这次构建大顶堆。将元素频率对(例如(频率, 元素))压入大顶堆中。如果大顶堆的大小超过了k,则弹出堆顶元素(因为大顶堆的堆顶是当前堆中频率最大的元素对)。与小顶堆不同的是,这里在遍历完哈希表后,需要将大顶堆中的元素全部弹出并反转顺序,才能得到出现频率前k高的元素。因为大顶堆中堆顶是频率最高的元素,而我们要的是按任意顺序的前k高频率元素,所以弹出并反转顺序后就可以得到符合要求的结果。
- 同样创建一个大小为
-
-
提取前 k 高频率元素:
-
对于构建小顶堆的方法,直接从小顶堆中取出元素部分(即每个元组中的第二个元素),组成列表返回即可。例如,小顶堆中剩下的元素对可能是
[(3, 1), (2, 2)],则返回的列表为[1, 2]。 -
对于构建大顶堆的方法,将大顶堆中的元素全部弹出并反转顺序后,取出元素部分组成列表返回。例如,大顶堆弹出并反转顺序后的元素对可能是
[(3, 1), (2, 2)],同样返回列表[1, 2]。
import java.util.*; public class Main { public static String solution(int[] nums, int k) { // 使用哈希表记录每个元素的频率 Map<Integer, Integer> freqMap = new HashMap<>(); for (int num : nums) { freqMap.put(num, freqMap.getOrDefault(num, 0) + 1); }
// 将频率存储到列表中并按频率排序 List<Map.Entry<Integer, Integer>> resultList = new ArrayList<>(freqMap.entrySet()); resultList.sort((a, b) -> b.getValue().compareTo(a.getValue())); // 按频率降序排序 // 创建结果字符串 StringBuilder sb = new StringBuilder(); for (int i = 0; i < k; i++) { sb.append(resultList.get(i).getKey()); if (i < k - 1) { sb.append(","); // 添加逗号分隔 } } return sb.toString(); // 返回结果字符串 } public static void main(String[] args) { // 测试用例 int[] nums1 = {1, 1, 1, 2, 2, 3}; int[] nums2 = {1}; // 输出结果是否与预期相符 System.out.println(solution(nums1, 2).equals("1,2")); // true System.out.println(solution(nums2, 1).equals("1")); // true }}
-