思想:利用快速排序的思想,从数组S中随机找出一个元素X,把数组分为两部分Sa和Sb。Sa中的元素大于等于X,Sb中元素小于X。这时有两种情况:
1. Sa中元素的个数小于k,则Sb中的第k-|Sa|个元素即为第k大数;
2. Sa中元素的个数大于等于k,则返回Sa中的第k大数。
时间复杂度近似为O(n)。
#include <iostream>
using namespace std;
// 快速排序的一趟操作
int quickSortOneTime(int array[], int low, int high) {
int key = array[low]; // 选定基准元素
while (low < high) {
// 从右侧开始扫描,找到小于基准值的元素
while (key < array[high] && low < high) high--;
array[low] = array[high]; // 将大于基准值的元素放到左边
// 从左侧开始扫描,找到大于基准值的元素
while (key > array[low] && low < high) low++;
array[high] = array[low]; // 将小于基准值的元素放到右边
}
// 最后,将基准元素放到其正确的位置
array[high] = key;
return high; // 返回基准元素的位置
}
// 选择第k小的元素
int Select_k(int array[], int low, int high, int k) {
int index;
// 基本情况,当low == high时,说明只剩一个元素,返回该元素
if (low == high) return array[low];
// 进行一趟快速排序
int partition = quickSortOneTime(array, low, high);
index = partition - low + 1; // 计算当前partition位置的元素是第几个小值
if (index == k) {
// 如果当前元素是第k小的元素,返回它
return array[partition];
} else if (index < k) {
// 如果第k小的元素在右半部分,递归查找右半部分
return Select_k(array, partition + 1, high, k - index);
} else {
// 如果第k小的元素在左半部分,递归查找左半部分
return Select_k(array, low, partition - 1, k);
}
}
int main() {
int arr[] = {12, 3, 5, 7, 19, 2};
int n = sizeof(arr) / sizeof(arr[0]);
int k = 4; // 要查找的第k小元素(例如,第4小元素)
int result = Select_k(arr, 0, n - 1, k);
cout << "The " << k << "th smallest element is " << result << endl;
return 0;
}
代码说明:
-
quickSortOneTime
函数:进行一次分区操作。选定数组的第一个元素作为基准元素(key),然后通过左右两边的指针扫描数组,确保基准元素在它应该处于的排序位置。返回基准元素最终的位置。 -
Select_k
函数:递归寻找数组中的第k
小元素。每次调用quickSortOneTime
执行一次分区,将数组分成两部分。如果基准元素刚好是第k
小元素,直接返回它;如果不是,则根据k
的位置递归搜索左半部分或右半部分。
测试结果:
对于给定的数组 {12, 3, 5, 7, 19, 2}
和 k = 4
,输出将会是:
The 4th smallest element is 7