LeetCode/347. 前 K 个高频元素

347. 前 K 个高频元素

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

示例1:

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

示例2:

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

提示:

  • 你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
  • 你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。
  • 题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的。
  • 你可以按任意顺序返回答案。

来源:力扣(LeetCode)
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

题解:

本题用最直观的思路,就是创建一个Map,通过计数、排序,可以得到每个数字按照出现次数的降序排列的HashMap,然后截取前k个返回就可以得到结果。

具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> map = new HashMap<>();
//计数
for (int i = 0; i < nums.length; i++) {
if (map.containsKey(nums[i])) {
Integer times = map.get(nums[i]);
times += 1;
map.put(nums[i], times);
} else {
map.put(nums[i], 1);
}
}
//按value降序排序
List<Map.Entry<Integer, Integer>> list = new ArrayList<>(map.entrySet());
Collections.sort(list, (o1, o2) -> o2.getValue().compareTo(o1.getValue()));
int[] result = new int[k];
Iterator<Map.Entry<Integer, Integer>> iterator = list.iterator();
//包装结果
for (int i = 0; i < k; i++) {
Map.Entry<Integer, Integer> next = iterator.next();
result[i] = next.getKey();
}
return result;
}
}

当然了,这种暴力解法时间复杂度和空间复杂度都不太好,我们利用官方描述的堆的方式来解题。一顿操作后,发现时间复杂度和空间复杂度依旧很高。

具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class Solution {
public int[] topKFrequent(int[] nums, int k) {
//建立出现评率数组
Map<Integer, Integer> map = new HashMap<>();
for (int num : nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
}
//对频率数组进行排序
List<Map.Entry<Integer, Integer>> list = new ArrayList<>(map.entrySet());
Collections.sort(list, (o1, o2) -> o2.getValue().compareTo(o1.getValue()));

//建立小顶堆
PriorityQueue<int[]> queue = new PriorityQueue<>(Comparator.comparingInt(o -> o[1]));
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
int key = entry.getKey();
int value = entry.getValue();
if (queue.size() == k) {
//比较新元素和堆顶元素的大小,如果堆的大小为k且堆顶元素的值比新值小,则将替换堆顶元素
if (queue.peek()[1] < value) {
queue.poll();
queue.offer(new int[]{key, value});
}
} else {
queue.offer(new int[]{key, value});
}
}

//包装结果
int[] result = new int[k];
for (int i = 0; i < k; i++) {
result[i] = queue.poll()[0];
}
return result;
}
}

还有一种桶排序的方法,桶排序的基本思想就是,首先我们还是要得到出现频次数组,然后我们需要新建一个桶数组,将桶数组的下标和出现频次对应起来,比如1出现了3次,那么桶数组下标为3的位置就应该存放1,最后,我们倒序遍历桶数组,就可以得到出现频次前K大的结果。

具体代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Solution {
public int[] topKFrequent(int[] nums, int k) {
//建立出现频率数组
Map<Integer, Integer> map = new HashMap<>();
for (int num : nums) {
map.put(num, map.getOrDefault(num, 0) + 1);
}

List<Integer>[] freqList = new List[nums.length + 1];
for (int i = 0; i < freqList.length; i++) {
freqList[i] = new ArrayList<>();
}

//将出现频次与桶数组下标对应
map.forEach((num, freq) -> freqList[freq].add(num));

//包装结果
int[] result = new int[k];
int index = 0;
for (int i = freqList.length - 1; i > 0; i--) {
for (Integer num : freqList[i]) {
result[index++] = num;
if (index == k) {
return result;
}
}
}
return result;
}
}

Comments