P1923 【深基9.例4】求第 k 小的数
做不出来根本原因在于对快速排序理解不彻底
快排代码
int randint(int l, int r){ // 生成在 [l, r] 之间的随机数
return rand() % (r - l + 1) + l;
}
void qsort(int l, int r){ // l 为左端点,r 为右端点
if(l >= r){ // 如果长度为 0 就返回
return;
}
// 给大家讲解一下为什么此时可以不用判长度为 1 的序列:
// 因为序列中的这个数在添加的过程中会自动被分到 c 数组中去,而 c 在之后是不用排序的,相当于什么都没做,当然也可以在这里判一下长度为 1 的情况,直接 return 就可以了
int num = randint(l, r), ind1 = 0, ind2 = 0, ind3 = 0; // 随机选择一个数,并定义三个作为下标的变量来记录长度、存放数据
for(int i = l;i <= r;i++){ // 将 a 中的数分别分到 b, c, d(如上所述)
if(a[i] < a[num]){
b[ind1++] = a[i];
}
else if(a[i] == a[num]){
c[ind2++] = a[i];
}
else{
d[ind3++] = a[i];
}
}
for(int i = 0;i < ind1;i++){ // 将 b, c, d 中的数重新放回 a
a[i + l] = b[i];
}
for(int i = 0;i < ind2;i++){
a[i + ind1 + l] = c[i];
}
for(int i = 0;i < ind3;i++){
a[i + ind1 + ind2 + l] = d[i];
}
qsort(l, l + ind1 - 1); // 继续排序原来的 b 和 d
qsort(l + ind1 + ind2, r);
其实就是分治思想,把所有的区间分为左中右,小于中都去左,大于中都去右。
这题求一个数列中第k小的数完全可以用类似的思想。
/*********************************************************************
程序名:
版权:
作者:
日期: 2023-10-06 09:10
说明:
*********************************************************************/
#include <iostream>
using namespace std;
int array[5000001], n, k;
void preprocessing() {
scanf("%d %d", &n, &k);
for (int i = 0; i < n; i++) {
scanf("%d", &array[i]);
}
}
void solve(int l, int r) {
int tl = l, tr = r, mid = array[(l + r) / 2];
do {
while (array[tl] < mid)
tl++;
while (array[tr] > mid)
tr--;
if (tl <= tr) {
swap(array[tl], array[tr]);
tl++;
tr--;
}
} while (tl <= tr);
if (k <= tr)
solve(1, tl);
else if (k >= tl)
solve(tl, r);
else {
printf("%d", array[tr + 1]);
}
}
int main() {
preprocessing();
solve(0, n - 1);
return 0;
}