线性时间选择算法通常指的是能在 $O(n)$ 时间复杂度内从无序数组中找到第 $k$ 小(或第 $k$ 大)元素的算法,例如随机选择算法和中位数的中位数(BFPRT)算法。
### 随机选择算法
随机选择算法的基本思想是随机选择一个基准元素,将数组划分为两部分,然后根据基准元素的位置与 $k$ 的大小关系,决定在左半部分或右半部分继续查找。在 C++ 实现中,该算法的空间复杂度为 $O(1)$。这是因为在算法执行过程中,只需要常数级的额外空间来进行元素交换和记录基准元素的位置等操作,不随输入规模 $n$ 的增长而增长。以下是一个简单的 C++ 代码示例:
```cpp
#include <iostream>
#include <vector>
#include <cstdlib>
#include <ctime>
// 交换两个元素
void swap(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
// 分区函数
int partition(std::vector<int>& arr, int left, int right) {
int pivotIndex = left + rand() % (right - left + 1);
int pivot = arr[pivotIndex];
swap(arr[pivotIndex], arr[right]);
int i = left - 1;
for (int j = left; j < right; j++) {
if (arr[j] <= pivot) {
i++;
swap(arr[i], arr[j]);
}
}
swap(arr[i + 1], arr[right]);
return i + 1;
}
// 随机选择算法
int randomSelect(std::vector<int>& arr, int left, int right, int k) {
if (left == right) return arr[left];
int pivotIndex = partition(arr, left, right);
int rank = pivotIndex - left + 1;
if (k == rank) return arr[pivotIndex];
else if (k < rank) return randomSelect(arr, left, pivotIndex - 1, k);
else return randomSelect(arr, pivotIndex + 1, right, k - rank);
}
int main() {
srand(time(0));
std::vector<int> arr = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
int k = 5;
int result = randomSelect(arr, 0, arr.size() - 1, k);
std::cout << "第 " << k << " 小的元素是: " << result << std::endl;
return 0;
}
```
### 中位数的中位数(BFPRT)算法
BFPRT 算法是一种确定性的线性时间选择算法,它通过一种特殊的方法选择基准元素,保证每次划分能将数组较好地分成两部分。在 C++ 实现中,该算法的空间复杂度同样为 $O(1)$。虽然在算法执行过程中可能会进行递归调用,但由于每次递归调用时,问题规模都会缩小,且递归栈的深度是常数级的,因此额外的空间开销仍然是常数级的。以下是一个简单的 C++ 代码示例:
```cpp
#include <iostream>
#include <vector>
#include <algorithm>
// 交换两个元素
void swap(int& a, int& b) {
int temp = a;
a = b;
b = temp;
}
// 插入排序
void insertionSort(std::vector<int>& arr, int left, int right) {
for (int i = left + 1; i <= right; i++) {
int key = arr[i];
int j = i - 1;
while (j >= left && arr[j] > key) {
arr[j + 1] = arr[j];
j--;
}
arr[j + 1] = key;
}
}
// 找到中位数的中位数
int medianOfMedians(std::vector<int>& arr, int left, int right) {
if (right - left < 5) {
insertionSort(arr, left, right);
return arr[(left + right) / 2];
}
for (int i = left; i <= right; i += 5) {
int subRight = std::min(i + 4, right);
insertionSort(arr, i, subRight);
swap(arr[left + (i - left) / 5], arr[(i + subRight) / 2]);
}
int newRight = left + (right - left) / 5;
return medianOfMedians(arr, left, newRight);
}
// 分区函数
int partition(std::vector<int>& arr, int left, int right, int pivot) {
for (int i = left; i <= right; i++) {
if (arr[i] == pivot) {
swap(arr[i], arr[right]);
break;
}
}
int i = left - 1;
for (int j = left; j < right; j++) {
if (arr[j] <= pivot) {
i++;
swap(arr[i], arr[j]);
}
}
swap(arr[i + 1], arr[right]);
return i + 1;
}
// BFPRT 算法
int bfprt(std::vector<int>& arr, int left, int right, int k) {
if (left == right) return arr[left];
int pivot = medianOfMedians(arr, left, right);
int pivotIndex = partition(arr, left, right, pivot);
int rank = pivotIndex - left + 1;
if (k == rank) return arr[pivotIndex];
else if (k < rank) return bfprt(arr, left, pivotIndex - 1, k);
else return bfprt(arr, pivotIndex + 1, right, k - rank);
}
int main() {
std::vector<int> arr = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};
int k = 5;
int result = bfprt(arr, 0, arr.size() - 1, k);
std::cout << "第 " << k << " 小的元素是: " << result << std::endl;
return 0;
}
```
综上所述,线性时间选择算法在 C++ 实现中的空间复杂度通常为 $O(1)$。