1.基本思想
线性时间选择算法(Linear-time selection algorithm)的一个经典例子是 "QuickSelect",其基本思想与 "QuickSort" 快速排序算法相似。线性时间选择算法的目标是找到一个无序列表中第 k 小的元素,而不是对整个列表进行排序。
以下是线性时间选择算法的基本思想:
-
选择主元:从列表中选择一个“主元”(pivot)元素。这个主元的选择方法有很多种,可以是随机选择,或者使用一个称为“中位数的中位数”的方法。
-
划分:与快速排序一样,按照这个主元将列表划分为两个子列表,一个包含小于主元的元素,另一个包含大于主元的元素。
-
确定主元的位置:通过与子列表的大小进行比较,我们可以确定主元在整个列表中的位置。这样,我们就知道第 k 小的元素是在主元的左边、右边,还是就是主元本身。
-
递归或结束:
- 如果主元的位置就是 k,那么主元就是我们要找的元素。
- 如果 k 小于主元的位置,那么我们只需在左子列表中递归地查找第 k 小的元素。
- 如果 k 大于主元的位置,那么我们需要在右子列表中递归地查找第 k 小的元素(但请注意,这时我们在右子列表中查找的并不是第 k 小的元素,而是第 −主元位置k−主元位置 小的元素)。
1.1中位数的中位数算法的基本思想
-
分组:将输入列表分为 �/5n/5 个组,每组5个元素(最后一组可能包含少于5个元素)。
-
找到每个组的中位数:对每个组的元素进行排序(因为每组只有5个元素,所以可以使用任何算法,例如插入排序),然后选择每组的中位数。
-
递归地找到中位数的中位数:使用递归的方式,将步骤1和2应用于刚才找到的每组的中位数,直到找到所有中位数的中位数。这个中位数的中位数是我们的主元。
-
使用主元进行划分:与快速选择和快速排序类似,使用找到的主元将原列表划分为两部分。
2.C++代码
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 函数原型声明
int select(vector<int>&, int);
// 寻找一个小数组的中位数
int medianOfMedians(vector<int>& arr, int start, int end) {
int n = end - start + 1;
// 如果数组长度小于等于5,直接排序并返回中位数
if (n <= 5) {
sort(arr.begin() + start, arr.begin() + end + 1);
return arr[start + n / 2];
}
// 分割数组成小的5个元素的部分,并找到每个部分的中位数
vector<int> medians;
for (int i = 0; i < n / 5; i++) {
int median = medianOfMedians(arr, start + i * 5, start + i * 5 + 4);
medians.push_back(median);
}
// 对中位数数组进行递归调用,找到中位数的中位数
return select(medians, medians.size() / 2);
}
// 使用中位数的中位数策略选择第k小的元素
int select(vector<int>& arr, int k) {
int n = arr.size();
if (n <= 5) {
sort(arr.begin(), arr.end());
return arr[k];
}
// 找到中位数的中位数
int pivot = medianOfMedians(arr, 0, n - 1);
vector<int> lows, highs;
for (int num : arr) {
if (num < pivot) lows.push_back(num);
else if (num > pivot) highs.push_back(num);
}
if (k < lows.size()) return select(lows, k);
else if (k >= n - highs.size()) return select(highs, k - (n - highs.size()));
else return pivot;
}
int main() {
vector<int> arr = { 10, 4, 5, 8, 6, 11, 26, 3, 7 };
cout << "原始数组:";
for (int num : arr) {
cout << num << " ";
}
cout << endl << "数组的长度是: " << arr.size() << endl; //统计数组中元素的个数
int k; // 第k小的元素
cout << "请输入k的值:";
cin >> k ;
cout <<"第" << k << "小的数是: " << select(arr, k-1) << endl; //k-1:下标一般从0开始计算
return 0;
}