快速选择问题
快速选择问题。输入 n 个整数和一个正整数
k(1≤k≤n) ,输出这些整数从小到大排序后的第 k 个(例如,k=1 就是最小值)。 n≤107 。
从题目上看,其n的的取值范围为 [1,107] ,由之前的那张算法时间复杂度选择表,可以看出,使用 O(nlog2n) 及以上时间复杂度超时的可能性非常大,这时候最好的选取 O(n) 及以下时间复杂度的算法。
因为是选取一个序列中第
k
大的元素,那么无疑会想到使用分治法进行求解,如果采用像归并排序这样的分治方法,那么时间复杂度毫无疑问是
-
分治三部曲
- 划分问题:通过标兵元素,将序列划分为左右两部分,左边的元素都小于等于标兵,右边的元素都大于等于标兵。
-
递归求解:假设
A[p...r]
,划分后左部分为
A[p...q]
右部分为
A[q+1...r]
,将
k
与划分后
A[q−p+1] 进行比较,小于则只在左部分递归,大于则只在右部分递归。 - 合并问题:无合并过程。
实现主程序
#include <iostream>
#include <algorithm>
#include <ctime>
#include <fstream>
using namespace std;
// 快速排序算法
int qSort(int *a, int lef, int righ, int k) {
// 递归边界
if(lef > righ) {
return 0;
}
// 取标兵值
int centerV = a[lef + (righ - lef) / 2];
// 大于标兵值的元素放在标兵值右边
// 小于标兵值的元素放在标兵值左边
int i = lef;
int j = righ;
while(i <= j) {
// 从左往右扫描到大于标兵值的元素
for(; i <= j; i++) {
if(a[i] >= centerV) {
break;
}
}
// 从右往左扫描到小于标兵值的元素
for(; j >= i; j--) {
if(a[j] <= centerV) {
break;
}
}
// 退出条件
if(i > j) {
break;
}
// 交换
swap(a[i], a[j]);
i++;
j--;
}
if(k - 1 <= i) {
// 递归求解左半
return qSort(a, lef, j, k);
} else if(k - 1 > i + 1) {
// 递归求解右半
return qSort(a, i, righ, k);
} else{
return a[k - 1];
}
}
int quickSort(int *a, int n, int k) {
return qSort(a, 0, n - 1, k);
}
int main() {
// 测试数据(小量)
// int a[] = {4432, 3, -3, 5, 5, 3, 5435, -11, 3423, -1, -4421, 34432};
// int a[] = {1,2,3,4,9,8,7,8,9,10};
int a[] = {1,2,3,4,9,33,12,8,9,10};
int n = 10;
int k = 9;
cout << "排序之前:";
for(int i = 0; i < n; i++) {
cout << a[i] << " ";
}
cout << endl;
cout << "第" << k << "个数是:" << quickSort(a, n, k) << endl;
cout << "排序之后:";
for(int i = 0; i < n; i++) {
cout << a[i] << " ";
}
cout << endl;
return 0;
}
输出数据
排序之前:1 2 3 4 9 33 12 8 9 10
第9个数是:12
排序之后:1 2 3 4 9 8 9 10 12 33
Process returned 0 (0x0) execution time : 0.037 s
Press any key to continue.