从arr[1,n]n个数中,找到最大的K个数。
1.排序 时间复杂度 n*lgn
2.局部排序
冒泡
每冒一次泡,找到一个最大值,直到k个。时间复杂度n*k。
堆
只找到topk,不排序topk。
先用k个元素生成一个小顶堆,这个小顶堆用于存储当前最大的k个元素;
接着,从第k+1个元素开始扫描,和堆顶元素进行比较,如果被扫描的元素大于堆顶,则替换堆顶元素,并调整堆;
时间复杂度n*lgk
随机选择(减治)
时间复杂度 n
找到第k大的数,进行partition。
i = partition(arr, 1, n);
- 如果i大于k,则说明arr[i]左边的元素都大于k,于是只递归arr[1, i-1]里第k大的元素即可;
- 如果i小于k,则说明说明第k大的元素在arr[i]的右边,于是只递归arr[i+1, n]里第k-i大的元素即可
分治:快排,大问题分解为小问题,小问题要递归各个分支
减治:二分查找,随机选择。大问题分解为小问题,小问题只需要递归一个分支。
扩展:找出前K个出现频率最高的数
class Solution { public List<Integer> topKFrequent(int[] nums, int k) { // 使用字典,统计每个元素出现的次数,元素为键,元素出现的次数为值 HashMap<Integer,Integer> map = new HashMap(); for(int num : nums){ if (map.containsKey(num)) { map.put(num, map.get(num) + 1); } else { map.put(num, 1); } } // 遍历map,用最小堆保存频率最大的k个元素 PriorityQueue<Integer> pq = new PriorityQueue<>(new Comparator<Integer>() { @Override public int compare(Integer a, Integer b) { return map.get(a) - map.get(b); } }); for (Integer key : map.keySet()) { if (pq.size() < k) { pq.add(key); } else if (map.get(key) > map.get(pq.peek())) { pq.remove(); pq.add(key); } } // 取出最小堆中的元素 List<Integer> res = new ArrayList<>(); while (!pq.isEmpty()) { res.add(pq.remove()); } return res; }