【分治算法2.9】线性时间选择(C++实现)

1.基本思想

线性时间选择算法(Linear-time selection algorithm)的一个经典例子是 "QuickSelect",其基本思想与 "QuickSort" 快速排序算法相似。线性时间选择算法的目标是找到一个无序列表中第 k 小的元素,而不是对整个列表进行排序。

以下是线性时间选择算法的基本思想:

  1. 选择主元:从列表中选择一个“主元”(pivot)元素。这个主元的选择方法有很多种,可以是随机选择,或者使用一个称为“中位数的中位数”的方法。

  2. 划分:与快速排序一样,按照这个主元将列表划分为两个子列表,一个包含小于主元的元素,另一个包含大于主元的元素。

  3. 确定主元的位置:通过与子列表的大小进行比较,我们可以确定主元在整个列表中的位置。这样,我们就知道第 k 小的元素是在主元的左边、右边,还是就是主元本身。

  4. 递归或结束

    • 如果主元的位置就是 k,那么主元就是我们要找的元素。
    • 如果 k 小于主元的位置,那么我们只需在左子列表中递归地查找第 k 小的元素。
    • 如果 k 大于主元的位置,那么我们需要在右子列表中递归地查找第 k 小的元素(但请注意,这时我们在右子列表中查找的并不是第 k 小的元素,而是第 −主元位置k−主元位置 小的元素)。

 1.1中位数的中位数算法的基本思想

  1. 分组:将输入列表分为 �/5n/5 个组,每组5个元素(最后一组可能包含少于5个元素)。

  2. 找到每个组的中位数:对每个组的元素进行排序(因为每组只有5个元素,所以可以使用任何算法,例如插入排序),然后选择每组的中位数。

  3. 递归地找到中位数的中位数:使用递归的方式,将步骤1和2应用于刚才找到的每组的中位数,直到找到所有中位数的中位数。这个中位数的中位数是我们的主元。

  4. 使用主元进行划分:与快速选择和快速排序类似,使用找到的主元将原列表划分为两部分。

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;
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值