标签:
3 kth
3.1 Description
给定 n 个不超过 10^9 的正整数,请线性时间选择算法 (linear select)求其中的第 k 大值。
3.2 Input
第一行两个整数 n,k。 第二行 n 个整数,表示题目中的那 n 个正整数。
3.3 Output
一行,表示答案。
3.4 Sample Input
10 3
2 4 7 3 5 6 9 6 1 8
3.5 Sample Output
7
3.6 Constraints
一共 10 个测试点,每个测试点 10 分,只有当你的答案与标准答案完全一致时才能得到 10 分,否则为 0 分。 对于 30% 的数据,n ≤ 1000。 对于 50% 的数据,n ≤ 100000。 对于 100% 的数据,1 ≤ n ≤ 106,1 ≤ k ≤ n。 时限制: 1s / 256M
被大神的linear select神到,自己今天又写了个quick select 一起贴上 %%%队长%%%w老师
linear select 的思想是把一个序列分成5个一组的区间,取每个区间的中位数,再取这些中位数中的中位数,有证明称其不小于区间1/4,不大于区间3/4,这样我们就缩小了问题规模,同时,求[中位数的中位数时]我们仍可以用到求区间第k大(小)数的算法,这样我们可以用上递归结构。
1 //linear select O(100n) 2 #include <stdio.h> 3 #include <stdlib.h> 4 5 const int MAXN = 1000005; 6 7 int a[MAXN]; 8 9 int __cnt; 10 11 inline void sort(int *a, int n) { //为了证明这个算法效率很高老师用了最慢的选择排序%%% 12 for (int i = 1; i < n; i++) { 13 for (int j = i + 1; j <= n; j++) { 14 ++__cnt; 15 if (a[i] > a[j]) { 16 int tmp = a[i]; 17 a[i] = a[j]; 18 a[j] = tmp; 19 } 20 } 21 } 22 } 23 24 int trivial_select(int *a, int n, int k) {//对一个区间排序,取中位数 25 sort(a, n); 26 return a[k]; 27 } 28 29 // a[]: 1-based 30 int select(int *a, int n, int k) { 31 static const int Q = 5;//区间的划分取奇数 32 if (n <= Q) { 33 return trivial_select(a, n, k); 34 } 35 int *b = new int[(n - 1) / Q + 1]; 36 int c[Q]; 37 int cnt = (n - 1) / Q + 1; 38 for (int i = 0; i < cnt; i++) { 39 for (int j = 0; j < Q; j++) { 40 c[j] = i * Q + j < n ? a[i * Q + j + 1] : 2147483647; 41 } 42 b[i] = trivial_select(c - 1, Q, (Q + 1) / 2); 43 } 44 int x = select(b - 1, cnt, (cnt + 1) / 2);//再划分区间中位数的中位数 45 delete []b; 46 47 static int z[MAXN]; 48 int l = 0, r = n + 1; 49 for (int i = 1; i <= n; i++) { 50 if (a[i] < x) { 51 z[++l] = a[i]; 52 } else { 53 z[--r] = a[i]; 54 } 55 } 56 for (int i = 1; i <= n; i++) { 57 a[i] = z[i]; 58 } 59 if (k <= l) { 60 return select(a, l, k); 61 } else { 62 return select(a + l, n - l, k - l); 63 } 64 } 65 66 int main() { 67 freopen("kth.in", "r", stdin); 68 freopen("kth.out", "w", stdout); 69 int n, k; 70 scanf("%d%d", &n, &k); 71 for (int i = 1; i <= n; i++) { 72 scanf("%d", a + i); 73 } 74 printf("%d\n", select(a, n, n - k + 1)); 75 }
quick select的思路和快排类似,随机选择的数经历快排确定位置后,比它小的数都在它左边,比它大的数都在它右边,这样就可以缩小问题规模,递归出解,数据随机可以达到excepted O(nlogn)(常数未知)
#include <cstdio> #include <iostream> #include <algorithm> #include <cstdlib> #include <ctime> #define siji(i,x,y) for(int i=(x);i<=(y);i++) #define gongzi(i,x,y) for(int j=(x);j>=y;j++) #define maxn 1000005 #define random(x) (rand()%(x)) using namespace std; int n,a[maxn],k; int select(int l,int r) { if(l==r) {return a[l];} int mid=random(r-l+1)+l; int key=a[mid];//mid为最终下标,不断检索,直到满足左边的都比key小,右边都比key大 int first=l,last =r; while(first<last) { while(last>mid&&a[last]>=key) { last--; } swap(a[mid],a[last]);mid=last;//此时last指的数比mid小,可以拿mid与last交换 while(first<mid&&a[first]<=key) { first++; } swap(a[first],a[mid]);mid=first;//此时first指的数比mid大,同上 } if(k==mid) return a[mid]; else if(k<mid) return select(l,mid-1);//这里因为太弱出现了一个错误,就是没有写return……这可是一个int函数……真是太粗心了,不写的话会导致返回一个很大的野数 else if (mid+1<=r) return select(mid+1,r); } int main(int argc, char const *argv[]) { ios::sync_with_stdio(false); cin>>n>>k; siji(i,1,n) cin>>a[i]; srand((int)time(0));//利用时间作为随机数种子 cout<<select(1,n); return 0; }
大连廿四2016暑假集训day1-T3(quick select&linear select)
标签:
原文地址:http://www.cnblogs.com/ivorysi/p/5744904.html