1、快速排序法
基本思路:使用快速排序排列数组,然后取出前k个数
效率较低
import java.util.*;
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
int[] res =new int[k];
if(arr.length==0 || arr==null || arr.length < k) return res;
//快速排序
quicksort(arr,0,arr.length-1);
for(int i=0;i<k;i++){
res[i]=arr[i];
}
return res;
}
//排序主方法
private static void quicksort(int[] arr ,int lo,int hi){
if(hi<=lo) return;
int j =partition(arr,lo,hi); //寻找切分点(前面部分 < 切分点 < 后面部分 )
quicksort(arr,lo,j-1); //递归排序切分点 前面部分
quicksort(arr,j+1,hi); //递归排序切分点 后面部分
}
//切分方法
private static int partition(int[]arr,int lo,int hi){
int i =lo , j=hi+1; //两个指针,i从前面扫描 ,j从后面扫描
int p =arr[lo]; //将第一个元素作为切分点
while(true){
while(arr[++i] < p ) if(i==hi) break; // 从前往后,到大于p的值就停下
while(p < arr[--j]) if(j==lo) break; //从后往前,到小于p的值就停下
if(i>=j) break; // 如果两个指针重合,那么就退出
exch(arr,i,j); //交换两个指针对应的值
}
exch(arr,lo,j); //全部交换完后,将切分点放到正确的位置
return j; //返回切分点
}
//通用交换方法
private static void exch(int[] arr ,int lo ,int hi){
int temp =arr[lo];
arr[lo] = arr[hi];
arr[hi] =temp ;
}
}
2、快速选择(平均O(n)的复杂度)
利用快排切分的思想,每次切分都会返回一个下标,然后用下标和k进行对比
如果 j 等于 k
,那么下标 j 前面的元素就是最小的k个数 ;
如果 j 小于 k
,继续右边
区域切分;
如果 j 大于 k
,继续左边
区域切分;
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
if (k == 0 || arr.length == 0) {
return new int[0];
}
// 最后一个参数表示我们要找的是下标为k-1的数
return quickSearch(arr, 0, arr.length - 1, k - 1);
}
private int[] quickSearch(int[] nums, int lo, int hi, int k) {
// 每快排切分1次,找到排序后下标为j的元素,如果j恰好等于k就返回j以及j左边所有的数;
int j = partition(nums, lo, hi);
if (j == k) {
return Arrays.copyOf(nums, j + 1); //此时的下标为 j = k-1 的位置,即第k个
}
// 否则根据下标j与k的大小关系来决定继续切分左段还是右段。
return j > k? quickSearch(nums, lo, j - 1, k): quickSearch(nums, j + 1, hi, k);
}
// 快排切分,返回下标j,使得比nums[j]小的数都在j的左边,比nums[j]大的数都在j的右边。
public int partition(int[] nums, int lo, int hi) {
int v = nums[lo];
int i = lo, j = hi + 1;
while (true) {
while (++i <= hi && nums[i] < v);
while (--j >= lo && nums[j] > v);
if (i >= j) break;
exch(nums,i,j);
}
exch(nums,lo,j);
return j;
}
public static void exch(int[] arr ,int i, int j){
int t = arr[j];
arr[j] = arr[i];
arr[i] = t;
}
}
2、堆排序(最大堆的优先队列)
时间复杂度为O(nlgn)
class Solution {
public int[] getLeastNumbers(int[] arr, int k) {
if (k == 0 || arr.length == 0) return new int[0];
// 默认是小根堆,实现大根堆需要重写一下比较器。
Queue<Integer> pq = new PriorityQueue<>((v1, v2) -> v2 - v1);
for (int num: arr) {
if (pq.size() < k) {
pq.offer(num);
} else if (num < pq.peek()) {
pq.poll();
pq.offer(num);
}
}
// 返回堆中的元素
int[] res = new int[pq.size()];
int idx = 0;
for(int num: pq) {
res[idx++] = num;
}
return res;
}
}