平常人们选择数据中某个第几大的数据,往往使用先排序在选择的方法,这种方法的时间复杂度最快为Onlogn(快排时间复杂度),但在数据较大时往往会超出时间限制,故本次介绍时间复杂度为On的快速选择方法
快速选择算法:算法核心为递归和分治,和快速排序算法有一定的相似度,算法大概过程为
1.先判断区间的端点l,r和比较数pivot,pivot可取为arr[l],arr[r],arr[(l + r) / 2)]。
2.进行排序交换,使区间分为两部分,左边部分小于等于pivot,右边部分大于等于pivot。
3.递归排序左右边,当要求的第K个数,k >= sl,则递归排序右边,否则递归排序左边,如果区间只有一个数则返回。
具体算法如下:
public static int quickChoose(int l,int r,int [] arr,int k){
//如果当前只有一个数存在
if(l == r){
return arr[l];
}
//否则进行排序和查找
int i = l - 1,j = r + 1,pivot = arr[l];
//本次返回j,j + 1
//当前元素区间不止为一个数时
while(i < j){
while(arr[++i] < pivot);
while(arr[--j] > pivot);
//如果满足区间元素不为1
if(i < j){
int t = arr[j];
arr[j] = arr[i];
arr[i] = t;
}
}
//如果在左半部分,直接排序左边求解
int s1 = j - l + 1;
if(s1 >= k){
return quickChoose(l,i - 1,arr,k);
}else{
//否则排序右边数组
k = k - s1;
return quickChoose( i,r,arr,k);
}
}
注意:
1.本次算法应该用j作为分界点,否则不可进行排序分解(排序完成后,j在正中间)
2.数据读取时,scanner读取数据和c++和scanf和get基本用法相同,皆不可读取换行符和空格,若中间存储一次换行符,最好中间调用一次scanner.nextLine().方法。否则读入存在问题
题目链接为:活动 - AcWing
题解如下:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;
import java.util.Scanner;
public class QuickChoose {
static int n;
static int k;
public static void main(String[] args) throws IOException {
final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
final Scanner scanner = new Scanner(System.in);
n = scanner.nextInt();
k = scanner.nextInt();
int [] arr = new int[n];
//读入数据
// final String s1 = new Scanner(System.in).nextLine();
// String[] s = bufferedReader.readLine().split(" ");
for (int i = 0 ; i < n; i++){
arr[i] = scanner.nextInt();
}
// System.out.println(Arrays.toString(arr));
System.out.println(quickChoose(0,n - 1,arr,k));
bufferedReader.close();
}
//快速选择方法,该方法用于求出l到r区间范围内的第k个数
public static int quickChoose(int l,int r,int [] arr,int k){
//如果当前只有一个数存在
if(l == r){
return arr[l];
}
//否则进行排序和查找
int i = l - 1,j = r + 1,pivot = arr[l];
//本次返回j,j + 1
//当前元素区间不止为一个数时
while(i < j){
while(arr[++i] < pivot);
while(arr[--j] > pivot);
//如果满足区间元素不为1
if(i < j){
int t = arr[j];
arr[j] = arr[i];
arr[i] = t;
}
}
//如果在左半部分,直接排序左边求解
int s1 = j - l + 1;
if(s1 >= k){
return quickChoose(l,i - 1,arr,k);
}else{
//否则排序右边数组
k = k - s1;
return quickChoose( i,r,arr,k);
}
}
}