1.最高频 K 项问题
前导问题:前k大数
在一个整数数组中,找最大的k个数
这个问题有在线和离线两种解法:
- 离线算法:允许多遍遍历数组。排序后找的方法时间复杂度O(nlogn),但是需要找O(n)的解法。答案就是使用快速选择算法,先一遍遍历找到第k大的数,然后再一遍遍历找到前k大的数,总复杂度为o(n)
- 在线算法:数据以数据流进入,只允许一遍遍历。
View Code
public class topK { /** * @param nums: an integer array * @param k: An integer * @return: the top k largest numbers in array */ public int[] topkOffline(int[] nums, int k) { // 1.离线算法: // 首先一遍遍历找到第K大的数:快速选择算法O(n) int kLargestNum = QuickSelect(nums, k, 0, nums.length - 1); int [] res = new int[k]; int i = 0; for (Integer num : nums) { if (num >= kLargestNum && i < k) res[i++] = num; } return res; } public int[] topkOnline(int[] nums, int k) { // 2.在线算法:数据流形式进入,只允许一遍遍历: 使用最大堆 Comparator<Integer> comparator = new Comparator<Integer>() { @Override public int compare(Integer o1, Integer o2) { if (o1 > o2) return -1; else if (o1 < o2) return 1; else return 0; } }; PriorityQueue<Integer> pq = new PriorityQueue<>(k, comparator); for (Integer num : nums) { pq.add(num); } int[] res = new int[k]; for (int i = 0; i < k; i++) res[i] = pq.poll(); return res; } private int QuickSelect(int[] nums, int k, int start, int end) { if (k > nums.length || start > end) return -1; int pivot = end; int left = start; int right = end; while (left < right) { while (nums[left] <= pivot && left < right) left++; while (nums[right] >= pivot && left < right) right--; swap(nums, left, right); } swap(nums, left, pivot); if (k - 1 > left) QuickSelect(nums, k, left + 1, end); if (k - 1 < left) QuickSelect(nums, k, start, left - 1); return nums[left]; } private void swap(int[] nums, int x, int y) { int tmp = nums[x]; nums[x] = nums[y]; nums[y] = tmp; } public static void main(String[] args) { topK tk = new topK(); int [] nums = {6, 4, 10, 14, 3, 7, 2, 9}; int[] res = tk.topkOnline(nums, 4); for (Integer num : res) System.out.println(num); } }