1.问题
l 通过分治策略,选取第k小元素。
2.解析
通过快速排序和分治的思想,每次随机选取主元,判断小于主元的元素的个数,如果个数大于k则递归寻找左半段,如果个数小于k则递归寻找右半段
3.设计
1 int find_kth(int l, int r, int k) { 2 3 if (l == r)return a[l];//递归边界 4 5 int temp = a[random(l, r)];//随机选取主元 6 7 int cnt = 0;//计比主元大的个数 8 9 int i = l, j = r; 10 11 while (i <= j) { 12 13 while (a[i] > temp&& i <= j)i++, cnt++; 14 15 while (a[j] <= temp && i <= j)j--; 16 17 if (i < j) { 18 19 swap(a[i], a[j]); 20 21 cnt++; 22 23 } 24 25 i++, j--; 26 27 } 28 29 if (cnt >= k) {//若k<=cnt,就在左半段中递归寻找第k大 30 31 return find_kth(l, l + cnt - 1, k); 32 33 } 34 35 else {//否则在就在右半段中递归寻找第k-cnt大的数 36 37 return find_kth(l + cnt, r, k - cnt); 38 39 } 40 41 }
4.分析
时间复杂度:O(nlogn)
空间复杂度:O(n)
5.源码
https://github.com/BambooCertain/Algorithm.git

1 #include<stdio.h> 2 #include<algorithm> 3 #include<iostream> 4 using namespace std; 5 int a[100010]; 6 void swap(int& x, int& y) { 7 int temp = x; 8 x = y; 9 y = temp; 10 } 11 int random(int l, int r) {//返回一个在[l,r]范围内的随机整数 12 return rand() % (r - l + 1) + l; 13 } 14 int find_kth(int l, int r, int k) { 15 if (l == r)return a[l];//递归边界 16 int temp = a[random(l, r)];//随机选取主元 17 int cnt = 0;//计比主元大的个数 18 int i = l, j = r; 19 while (i <= j) { 20 while (a[i] > temp&& i <= j)i++, cnt++; 21 while (a[j] <= temp && i <= j)j--; 22 if (i < j) { 23 swap(a[i], a[j]); 24 cnt++; 25 } 26 i++, j--; 27 } 28 if (cnt >= k) {//若k<=cnt,就在左半段中递归寻找第k大 29 return find_kth(l, l + cnt - 1, k); 30 } 31 else {//否则在就在右半段中递归寻找第k-cnt大的数 32 return find_kth(l + cnt, r, k - cnt); 33 } 34 } 35 int main() { 36 int n; cin >> n; 37 for (int i = 1; i <= n; i++)cin >> a[i]; 38 int k; cin >> k; 39 cout << find_kth(1, n, k) << endl; 40 }