题目来源:【深基9.例4】求第 k 小的数 - 洛谷
解题思路:
解法1 对原数组进行快速排序,时间复杂度是 O(nlog2n)
#include<iostream>
#include<algorithm>
using namespace std;
int a[5000010], k;
int main()
{
ios_base::sync_with_stdio(false);//
cin.tie(NULL);
int n;
cin >> n >> k;
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
sort(a, a + n);
cout << a[k] << endl;
return 0;
}
解法2 变种的快速排序,时间复杂度为O(n),利用分治的思想,如果 k 在左边的范围,则递归左边的数组;如果在右边,则递归右边的部分;但是也有可能两边都不属于(比如说 j=4 时 i=6,但是你要 k=5),那么在递归时可以省略掉这部分。
#include<iostream>
#define maxn 5000010
using namespace std;
int a[maxn], n,k,ans = 0;
void findkth(int a[], int l, int r)
{
//引入数组的地址
if (l == r)
{
ans = a[l];//区间长度为1时,记录答案
return;
}
int i = l, j = r, flag = a[(l + r) / 2], tmp;
do {
while (a[i] < flag)i++;//从左边找比哨兵大的数
while (a[j] > flag)j--;//从右边找比哨兵小的数
if (i <= j)
{
//交换
tmp = a[i];
a[i] = a[j];
a[j] = tmp;
i++;
j--;
}
} while (i <= j);
if (k <= j)findkth(a, l, j);//第k小的数字在左区间
else if (i <= k)findkth(a, i, r);//第k小的数字在右区间
else findkth(a, j+1, i-1);//第k小的数字即不在左区间也不在右区间
}
int main()
{
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cin >> n >> k;
for (int i = 0; i < n; i++)
{
cin >> a[i];
}
findkth(a, 0, n - 1);
cout << ans << endl;
return 0;
}
下面代码,利用 srand
和 time
来设置随机种子,使用随机基准元素而不是中点元素,这样可以减少遇到最坏情况的概率。
#include<iostream>
#include<cstdlib> // 引入以使用rand()和srand()
#include<ctime> // 引入以使用time()
#define maxn 5000010
using namespace std;
int a[maxn], n, k;
int findkth(int a[], int l, int r, int k) {
if (l == r) {
return a[l];
}
// 随机选择基准
srand((unsigned)time(NULL));
int pivotIndex = l + rand() % (r - l + 1);
swap(a[pivotIndex], a[(l + r) / 2]);
int flag = a[(l + r) / 2], i = l, j = r, tmp;
do {
while (a[i] < flag) i++;
while (a[j] > flag) j--;
if (i <= j) {
tmp = a[i]; a[i] = a[j]; a[j] = tmp;
i++; j--;
}
} while (i <= j);
if (k <= j) return findkth(a, l, j, k);
else if (k >= i) return findkth(a, i, r, k);
return flag; // 当k在i和j之间时,说明找到了第k小的元素
}
int main() {
ios::sync_with_stdio(false); // 关闭cin和stdout的同步,这样可以加快I/O速度
cin.tie(NULL); // 解除cin与cout的绑定,这也可以稍微提高I/O性能
cin >> n >> k;
for (int i = 0; i < n; i++) {
cin >> a[i];
}
cout << findkth(a, 0, n - 1, k) << endl; // 直接打印返回的第k小元素
return 0;
}