剑指 Offer 40. 最小的k个数
*215. 数组中的第K个最大元素
*347. 前 K 个高频元素
给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:
输入: nums = [1], k = 1
输出: [1]
提示:
你可以假设给定的 k 总是合理的,且 1 ≤ k ≤ 数组中不相同的元素的个数。
你的算法的时间复杂度必须优于 O(n log n) , n 是数组的大小。
题目数据保证答案唯一,换句话说,数组中前 k 个高频元素的集合是唯一的。
你可以按任意顺序返回答案。
用map记录各个元素出现的次数,然后排序后得到频率最高的k个数
class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> map = new HashMap<>();
for(int temp : nums) {
map.put(temp, map.getOrDefault(temp, 0) + 1);
}
int len = map.size();
int[][] order = new int[len][2];
int index = 0;
for(int temp : map.keySet()) {
order[index][0] = temp;
order[index][1] = map.get(temp);
index++;
}
Arrays.sort(order, (a, b) -> b[1] - a[1]);
int[] ans = new int[k];
for(int i = 0; i < k; i++) {
ans[i] = order[i][0];
}
return ans;
}
}
最简单的做法是给「出现次数数组」排序。但由于可能有 O(N) 个不同的出现次数,故总的算法复杂度会达到 O(NlogN),不满足题目的要求。
可以利用堆的思想改进:维护一个大小为k的小顶堆,然后遍历「出现次数数组」
遍历完成后,堆中的元素就代表了「出现次数数组」中前 k 大的值。
下面是使用优先队列这个现成的类型实现的,也可以手动实现小顶堆
class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> occurrences = new HashMap<Integer, Integer>();
for (int num : nums) {
occurrences.put(num, occurrences.getOrDefault(num, 0) + 1);
}
// int[] 的第一个元素代表数组的值,第二个元素代表了该值出现的次数
PriorityQueue<int[]> queue = new PriorityQueue<int[]>(new Comparator<int[]>() {
public int compare(int[] m, int[] n) {
return m[1] - n[1];
}
});
for (Map.Entry<Integer, Integer> entry : occurrences.entrySet()) {
int num = entry.getKey(), count = entry.getValue();
if (queue.size() == k) {
if (queue.peek()[1] < count) {
queue.poll();
queue.offer(new int[]{
num, count});
}
} else {
queue.offer(new int[]{
num, count});
}
}
int[] ret = new int[k];
for (int i = 0; i < k; ++i) {
ret[i] = queue.poll()[0];
}
return ret;
}
}
class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> map = new HashMap<>();
for (int n : nums){
map.put(n, map.getOrDefault(n, 0) + 1);
}
PriorityQueue<Map.Entry<Integer, Integer>> queue = new PriorityQueue<>((e1, e2) -> e2.getValue() - e1.getValue());
queue.addAll(map.entrySet());
int[] ans = new int[k];
for (int i = 0; i < k && !queue.isEmpty(); ++i){
ans[i] = queue.poll().getKey();
}
return ans;
}
}
手动建堆
class Solution {
//记录数组中元素及其对应出现的次数
Map<Integer , Integer> map = new HashMap<>();
public int[] topKFrequent(int[] nums, int k) {
int[] heap = new int[k];
for(int i : nums){
map.put(i , map.getOrDefault(i,0) + 1);
}
Iterator it = map.keySet().iterator();
int i = 0;
while(it.hasNext()){
// 先初始化一个大小为k的堆,当堆中元素个数为k时,建立小根堆
if( i < k){
heap[i] = (Integer)it.next();
i++;
if(i == k){
for(int j = k/2 -1 ; j >= 0 ; j--){
heapSort(heap,j);
}
}
}else{
//小根堆建好后 ,对于每个新遍历的元素与堆顶比较,如果比堆顶大,替换堆顶,重新维持小根堆
int key = (Integer)it.next();
if(map.get(key) > map.get(heap[0])) heap[0] = key;
heapSort(heap , 0);
}
}
return heap;
}
//维持小根堆
//注意,堆中元素比较是比较它们出现的次数,即该元素在map中对应的value
public void heapSort(int[] heap , int i){
int temp = heap[i];
for(int j = 2*i + 1 ; j < heap.length ; j = 2*j + 1){
if(j+1 < heap.length && map.get(heap[j+1]) < map.get(heap[j])) j++;
if(map.get(heap[j]) < map.get(temp)){
heap[i] = heap[j];
i = j;
}else break;
}
heap[i] = temp;
}
}
在对数组 arr[l…r] 做快速排序的过程中,首先将数组划分为两个部分arr[i…q−1] 与 arr[q+1…j],并使得 arr[i…q−1] 中的每一个值都不超过 arr[q],且 arr[q+1…j] 中的每一个值都大于 arr[q]。
根据 k 与左侧子数组 arr[i…q−1] 的长度(为 q−i)的大小关系:
class Solution {
public int[] topKFrequent(int[] nums, int k) {
Map<Integer, Integer> occurrences = new HashMap<Integer, Integer>();
for (int num : nums) {
occurrences.put(num, occurrences.getOrDefault(num, 0) + 1);
}
List<int[]> values = new ArrayList<int[]>();
for (Map.Entry<Integer, Integer> entry : occurrences.entrySet()) {
int num = entry.getKey(), count = entry.getValue();
values.add(new int[]{
num, count});
}
int[] ret = new int[k];
qsort(values, 0, values.size() - 1, ret, 0, k);
return ret;
}
public void qsort(List<int[]> values, int start, int end, int[] ret, int retIndex, int k) {
int picked = (int) (Math.random() * (end - start + 1)) + start;
Collections.swap(values, picked, start);
int pivot = values.get(start)[1];
int index = start;
for (int i = start + 1; i <= end; i++) {
if (values.get(i)[1] >= pivot) {
Collections.swap(values, index + 1, i);
index++;
}
}
Collections.swap(values, start, index);
if (k <= index - start) {
qsort(values, start, index - 1, ret, retIndex, k);
} else {
for (int i = start; i <= index; i++) {
ret[retIndex++] = values.get(i)[0];
}
if (k > index - start + 1) {
qsort(values, index + 1, end, ret, retIndex, k - (index - start + 1));
}
}
}
}