0 引言
问题:从无序数组中选择第k小的元素。
1 随机选择法
1.1 算法步骤:
-
选择基准元素:随机选择一个元素作为基准。
-
分区:对数组进行分区,使得基准元素左边的所有元素都小于它,右边的所有元素都大于它。分区过程完成后,我们得到了基准元素在数组中的位置pivotIndex。
-
递归选择:
如果pivotIndex == k - 1(注意数组索引从0开始),则基准元素就是第k小的元素。
如果pivotIndex < k - 1,则第k小的元素在右子数组中,递归地在右子数组中寻找第(k - pivotIndex - 1)小的元素。
如果pivotIndex > k - 1,则第k小的元素在左子数组中,递归地在左子数组中寻找第k小的元素(注意此时k值不变,因为我们在子数组中重新计数)。
1.2 算法图解
1.3 算法复杂度
随机选择法通过随机选取基准元素进行划分,平均时间复杂度为O(n),最坏时间复杂度为O(n^2)
1.4 代码实现
点击查看代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <cassert>
using namespace std;/// <summary>
/// 输出向量
/// </summary>
/// <param name="c">输出流</param>
/// <param name="v">向量</param>
/// <returns>输出流</returns>
ostream& operator<<(ostream& c, vector<int> v)
{cout << "[ ";for (int a : v){cout << a << ' ';}cout << "]\n";return c;
}/// <summary>
/// 分割序列,默认使用a[p]分割,使小于a[p]元素位于左侧,反之位于右侧
/// </summary>
/// <param name="a">序列,对a具有破坏性</param>
/// <param name="p">左下标</param>
/// <param name="r">右下标</param>
/// <returns>分割点下标</returns>
int Partition(vector<int>& a, int p, int r)
{int i = p, j = r;int x = a[p];while (i<j){while (i < j && a[i] < x){++i;}while (i<j && a[j] > x){--j;}swap(a[i], a[j]);}a[j] = x;return j;
}/// <summary>
/// 随机选择
/// </summary>
/// <param name="a">线性序列,对a有破坏性</param>
/// <param name="p">左下标</param>
/// <param name="r">右下标</param>
/// <param name="k">第k小元素</param>
/// <returns>第k小元素值</returns>
int RandomizedSelect(vector<int>& a, int p, int r, int k)
{assert(k <= a.size());int nSplit = Partition(a, p, r);vector<int> vLeft(a.begin(), a.begin() + nSplit);vector<int> vRight(a.begin() + nSplit + 1, a.end());if (vLeft.size() == k - 1){return a[nSplit];}else if (k <= vLeft.size()){return RandomizedSelect(vLeft, 0, vLeft.size() - 1, k);}else{return RandomizedSelect(vRight, 0, vRight.size() - 1, k - vLeft.size() - 1);}
}int main()
{vector<int> vec({ 10,2,32,45,21,33,8,36,55,64,12,78,5 });cout << "原序列" << vec;int k = 5;int nV = RandomizedSelect(vec, 0, vec.size() - 1, k);sort(vec.begin(), vec.end());cout << "排序后:" << vec;cout << "第" << k << "小元素为:" << nV << endl;return 0;
}