leetcode上有不少topK的问题,而且topK问题也是面试必问的,所以在此做一个总结。
给定一个非空的整数数组,返回其中出现频率前 k 高的元素。
示例 1:
输入: nums = [1,1,1,2,2,3], k = 2
输出: [1,2]
示例 2:
输入: nums = [1], k = 1
输出: [1]
要求时间复杂度为O(nlogn)
堆排序思想 先统计每一个数字出现的次数,然后维护一个大小为k的小根堆,小根堆按照数字出现 的频率排序,当堆中元素小于k时,加入小根堆当中,如果小根堆元素=k,则比较堆顶与当前元素的频率,如果比堆顶小,说明,当前堆中已经有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 public int [] topKFrequent(int [] nums, int k) { Map<Integer, Integer> map = IntStream.of(nums).boxed().collect(Collectors.toMap(e -> e, e -> 1 , Integer::sum)); PriorityQueue<Integer> q = new PriorityQueue<>((a, b)->map.get(a) - map.get(b)); Set<Integer> integers = map.keySet(); for (int num: integers){ if (q.size() < k){ q.offer(num); }else { if ( map.get(q.peek()) < map.get(num)){ q.poll(); q.offer(num); } } } int i = 0 ; int [] res = new int [k]; while (!q.isEmpty()){ res[i++] = q.poll(); } return res; }
基于二叉搜索树 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 36 37 38 39 40 class Solution { public int [] topKFrequent(int [] nums, int k) { Map<Integer, Integer> counterMap = IntStream.of(nums).boxed().collect(Collectors.toMap(e -> e, e -> 1 , Integer::sum)); TreeMap<Integer, List<Integer>> treeMap = new TreeMap<>(); int count = 0 ; for (Map.Entry<Integer, Integer> entry: counterMap.entrySet()) { int num = entry.getKey(); int cnt = entry.getValue(); if (count < k) { treeMap.computeIfAbsent(cnt, ArrayList::new ).add(num); count++; } else { Map.Entry<Integer, List<Integer>> firstEntry = treeMap.firstEntry(); if (cnt > firstEntry.getKey()) { treeMap.computeIfAbsent(cnt, ArrayList::new ).add(num); List<Integer> list = firstEntry.getValue(); if (list.size() == 1 ) { treeMap.pollFirstEntry(); } else { list.remove(list.size() - 1 ); } } } } int [] res = new int [k]; int idx = 0 ; for (List<Integer> list: treeMap.values()) { for (int num: list) { res[idx++] = num; } } return res; } }
基于桶排序 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 public int [] topKFrequent1(int [] nums, int k) { Map<Integer, Integer> map = IntStream.of(nums).boxed().collect(Collectors.toMap(e -> e, e -> 1 , Integer::sum)); List<Integer>[] bucket = new ArrayList[nums.length+1 ]; for (int i = 0 ; i< bucket.length; i++){ bucket[i] = new ArrayList<>(); } map.forEach((key, count)->{ bucket[count].add(key); }); int [] res = new int [k]; int n = 0 ; for (int i = bucket.length-1 ; i > 0 ; i--){ for (int num : bucket[i]){ res[n++] = num; if (n == k){ return res; } } } return res; }
基于快速选择算法 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 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 public int [] topKFrequent2(int [] nums, int k) { Map<Integer, Integer> map = IntStream.of(nums).boxed().collect(Collectors.toMap(e -> e, e -> 1 , Integer::sum)); Pair[] p = new Pair[nums.length]; Pair[] pairs = IntStream.of(nums).distinct().boxed().map(num -> new Pair(num, map.get(num))).toArray(Pair[]::new ); Pair[] ans = qselect(pairs, 0 , pairs.length-1 , k-1 ); int [] res = new int [k]; int i = 0 ; for (Pair pair: ans){ res[i++] = pair.key; } return res; } private Pair[] qselect(Pair[] pairs, int l, int r, int aux) { if ( l <= r){ int p = partition(pairs, l, r); if ( p == aux){ return Arrays.copyOf(pairs, aux+1 ); }else if (p < aux){ return qselect(pairs, p+1 , r, aux); }else { return qselect(pairs, l, p-1 , aux); } } return new Pair[0 ]; } private int partition (Pair[] pairs, int l, int r) { int i = l; int j = r; int p = l; while (i < j){ while (pairs[i].count > pairs[p].count && i < j){ i++; } while (pairs[j].count <= pairs[p].count && i < j){ j--; } swap(pairs, i ,j); } swap(pairs, p, i); return i; } private void swap (Pair[] pairs, int l, int r) { Pair t = pairs[l]; pairs[l] = pairs[r]; pairs[r] = t; } class Pair { int key; int count; public Pair (int key, int count) { this .key = key; this .count = count; } }
基于快排 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 36 37 38 39 40 41 42 43 44 45 class Solution { public int [] getLeastNumbers(int [] arr, int k) { if (k == 0 || arr.length == 0 ) { return new int [0 ]; } return QuickSort(arr, 0 , arr.length-1 , k); } private int [] QuickSort(int [] arr, int low, int high, int k){ int partition = partition(arr, low, high); if (partition == k-1 ){ return Arrays.copyOf(arr, partition+1 ); } if (partition > k-1 ){ return QuickSort(arr, low, partition-1 , k); } else { return QuickSort(arr, partition+1 , high, k); } } private int partition (int [] arr, int low, int high) { int p = arr[low]; int i = low; while (low < high){ while (arr[high] > p && low < high) high--; while (arr[low] <= p && low < high) low++; swap(arr, low, high); } arr[i] = arr[low]; arr[low] = p; return low; } private void swap (int []arr, int i, int j) { int t = arr[i]; arr[i] = arr[j]; arr[j] = t; } }
直接排序 1 2 3 4 5 6 7 8 9 10 11 class Solution { public int [] getLeastNumbers(int [] arr, int k) { Arrays.sort(arr); int []res = new int [k]; int i = 0 ; for (int j = 0 ; j < k; j++){ res[i++] = arr[j]; } return res; } }
基于堆 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 public int [] getLeastNumbers(int [] arr, int k) { PriorityQueue<Integer> q = new PriorityQueue<>((a,b)->b-a); int [] res = new int [k]; if (k == 0 ){ return res; } for (int num: arr){ if (q.size() < k){ q.offer(num); }else { if ( q.peek() > num){ q.poll(); q.offer(num); } } } int i = 0 ; for (int num: q){ res[i++] = num; } return res; }
参考 https://leetcode-cn.com/problems/top-k-frequent-elements/solution/4-chong-fang-fa-miao-sha-topkji-shu-pai-xu-kuai-pa/