1.最高频 K 项问题
前导问题:前k大数
在一个整数数组中,找最大的k个数
这个问题有在线和离线两种解法:
- 离线算法:允许多遍遍历数组。排序后找的方法时间复杂度O(nlogn),但是需要找O(n)的解法。答案就是使用快速选择算法,先一遍遍历找到第k大的数,然后再一遍遍历找到前k大的数,总复杂度为o(n)
- 在线算法:数据以数据流进入,只允许一遍遍历。
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); } }
Top k Largest Numbers II
实现一个数据结构,提供下面两个接口:add(number)
添加一个元素;topk()
返回前K大的数
简单的很容易想到维护一个大顶堆。来一个数就add一个,然后返回topk。但这是很低效的算法。因为每次add操作的时间复杂度是O(logn),每次topk是O(klogn)。
public class topK2 { private int kSize; private PriorityQueue<Integer> pq; public topK2(int k) { 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; } }; pq = new PriorityQueue<>(k, comparator); kSize = k; } public void add(int num) { if (pq.size() < kSize) { pq.add(num); } else if (pq.size() == kSize) { int minNum = pq.peek(); if (num < minNum) return; else { pq.poll(); pq.add(num); } } } public List<Integer> topk() { List<Integer> res = new ArrayList<>(); Integer[] arr = new Integer[pq.size()]; Integer[] arr1 = pq.toArray(arr); Arrays.sort(arr1, Collections.reverseOrder()); res = Arrays.asList(arr1); return res; } }
最高频k项问题:
也分为离线算法和在线算法。
- 离线算法:hash+heap的方式来解决
public class Solution { /* * @param words: an array of string * @param k: An integer * @return: an array of string */ public String[] topKFrequentWords(String[] words, int k) { // write your code here Map<String, Integer> map = new HashMap<>(); for (String word : words) { if (!map.containsKey(word)) map.put(word, 1); else map.put(word, map.get(word) + 1); } PriorityQueue<String> pq = new PriorityQueue<>( (a, b) -> map.get(a) == map.get(b) ? b.compareTo(a) : map.get(a) - map.get(b)); for (String word : map.keySet()) { pq.add(word); if (pq.size() > k) pq.poll(); } LinkedList<String> res = new LinkedList<>(); while (!pq.isEmpty()) { res.addFirst(pq.poll()); } String[] ret = new String[res.size()]; ret = res.toArray(ret); return ret; } }
- 在线算法:在实时数据流中找到最常使用的k个单词.
实现TopK类中的三个方法:TopK(k)
, 构造方法add(word)
, 增加一个新单词topk()
, 得到当前最常使用的k个单词.
2.布隆过滤器
3.外排序算法
4.概率类大数据问题