CS-Notes/docs/notes/40. 最小的 K 个数.md

89 lines
2.6 KiB
Java
Raw Normal View History

2019-11-02 12:07:41 +08:00
# 40. 最小的 K 个数
[NowCoder](https://www.nowcoder.com/practice/6a296eb82cf844ca8539b57c23e6e9bf?tpId=13&tqId=11182&tPage=1&rp=1&ru=/ta/coding-interviews&qru=/ta/coding-interviews/question-ranking&from=cyc_github)
## 解题思路
### 快速选择
- 复杂度O(N) + O(1)
- 只有当允许修改数组元素时才可以使用
快速排序的 partition() 方法会返回一个整数 j 使得 a[l..j-1] 小于等于 a[j] a[j+1..h] 大于等于 a[j]此时 a[j] 就是数组的第 j 大元素可以利用这个特性找出数组的第 K 个元素这种找第 K 个元素的算法称为快速选择算法
```java
public ArrayList<Integer> GetLeastNumbers_Solution(int[] nums, int k) {
ArrayList<Integer> ret = new ArrayList<>();
if (k > nums.length || k <= 0)
return ret;
findKthSmallest(nums, k - 1);
/* findKthSmallest 会改变数组,使得前 k 个数都是最小的 k 个数 */
for (int i = 0; i < k; i++)
ret.add(nums[i]);
return ret;
}
public void findKthSmallest(int[] nums, int k) {
int l = 0, h = nums.length - 1;
while (l < h) {
int j = partition(nums, l, h);
if (j == k)
break;
if (j > k)
h = j - 1;
else
l = j + 1;
}
}
private int partition(int[] nums, int l, int h) {
int p = nums[l]; /* 切分元素 */
int i = l, j = h + 1;
while (true) {
while (i != h && nums[++i] < p) ;
while (j != l && nums[--j] > p) ;
if (i >= j)
break;
swap(nums, i, j);
}
swap(nums, l, j);
return j;
}
private void swap(int[] nums, int i, int j) {
int t = nums[i];
nums[i] = nums[j];
nums[j] = t;
}
```
### 大小为 K 的最小堆
- 复杂度O(NlogK) + O(K)
- 特别适合处理海量数据
应该使用大顶堆来维护最小堆而不能直接创建一个小顶堆并设置一个大小企图让小顶堆中的元素都是最小元素
维护一个大小为 K 的最小堆过程如下在添加一个元素之后如果大顶堆的大小大于 K那么需要将大顶堆的堆顶元素去除
```java
public ArrayList<Integer> GetLeastNumbers_Solution(int[] nums, int k) {
if (k > nums.length || k <= 0)
return new ArrayList<>();
PriorityQueue<Integer> maxHeap = new PriorityQueue<>((o1, o2) -> o2 - o1);
for (int num : nums) {
maxHeap.add(num);
if (maxHeap.size() > k)
maxHeap.poll();
}
return new ArrayList<>(maxHeap);
}
```
2019-11-02 14:39:13 +08:00
2019-11-02 17:33:10 +08:00
<div align="center"><img width="320px" src="https://cs-notes-1256109796.cos.ap-guangzhou.myqcloud.com/githubio/公众号二维码-2.png"></img></div>