您的位置:首页 > 其它

快速选择问题

2016-05-29 20:25 190 查看

快速选择问题

快速选择问题。输入n个整数和一个正整数k(1≤k≤n),输出这些整数从小到大排序后的第k个(例如,k=1就是最小值)。n≤107。

从题目上看,其n的的取值范围为[1,107],由之前的那张算法时间复杂度选择表,可以看出,使用O(nlog2n)及以上时间复杂度超时的可能性非常大,这时候最好的选取O(n)及以下时间复杂度的算法。

因为是选取一个序列中第k大的元素,那么无疑会想到使用分治法进行求解,如果采用像归并排序这样的分治方法,那么时间复杂度毫无疑问是O(nlog2n),这时,不妨想想和归并排序一样采用分治思想的快速排序。由于快速排序不需要合并步骤,且快速选择问题只需要递归一半,其时间复杂度为O(n)。

分治三部曲
划分问题:通过标兵元素,将序列划分为左右两部分,左边的元素都小于等于标兵,右边的元素都大于等于标兵。

递归求解:假设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.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息