求一个数组中第K小的数
2016-05-17 19:51
429 查看
面试南大夏令营的同学说被问到了这个问题,我的第一反应是建小顶堆,但是据他说用的是快排的方法说是O(n)的时间复杂度,
但是后来经过我的考证,这个算法在最坏的情况下是O(n^2)的,但是使用堆在一般情况下是O(n+klogn),最坏的情况是O(nlogn)
把两种方法整理一下,我还是推荐使用小顶堆的方法,因为它不仅可以求第K大,还可以求前K大。。。
一。快排。借用了快排的partition思想,其实是一种分治的方法。对于一个partition,他左边的数小于他,右边的数全大于他
那么:
1.如果他本身就是第K个数,直接返回。
2.如果他的下标是比K大的某个数,那么第K小数肯定出现在他左边。那么到partition的左边递归地求解
3.如果他的下标是比K小的某个数,那么第K小数肯定出现在他右边。那么到partition的右边递归地求解
唯一需要注意的地方是,要注意在递归的过程中,第K小数是一个相对值,即相对于区间[l,r]的左边界l;
二。小顶堆
使用堆可以求出最小的元素,通过不断地弹出元素,就可以找到第K大的元素
但是后来经过我的考证,这个算法在最坏的情况下是O(n^2)的,但是使用堆在一般情况下是O(n+klogn),最坏的情况是O(nlogn)
把两种方法整理一下,我还是推荐使用小顶堆的方法,因为它不仅可以求第K大,还可以求前K大。。。
一。快排。借用了快排的partition思想,其实是一种分治的方法。对于一个partition,他左边的数小于他,右边的数全大于他
那么:
1.如果他本身就是第K个数,直接返回。
2.如果他的下标是比K大的某个数,那么第K小数肯定出现在他左边。那么到partition的左边递归地求解
3.如果他的下标是比K小的某个数,那么第K小数肯定出现在他右边。那么到partition的右边递归地求解
唯一需要注意的地方是,要注意在递归的过程中,第K小数是一个相对值,即相对于区间[l,r]的左边界l;
#include <bits/stdc++.h> using namespace std; const int maxn = 1000; int a[maxn]; int n = 10,k = 6; int part(int low, int high) { int pivot = a[low]; while(low < high){ while(low < high&&a[high] >= pivot) high--; a[low] = a[high]; while(low < high&&a[low] <= pivot) low++; a[high] = a[low]; } a[low]=pivot; return low; } int quicksort(int l, int r, int k){ int pos = part(l,r); if(pos - l + 1 == k) return a[pos]; else if(pos - l + 1> k) return quicksort(l,pos-1,k); else return quicksort(pos+1,r,k-(pos-l+1)); } int main() { srand((unsigned)time(NULL)); for(int i = 0; i < n; ++i){ a[i] = rand()%(n<<1); } for(int i = 0; i < n; ++i) printf("%d ",a[i]); printf("\n"); int x = quicksort(0,n-1,k); printf("%d\n", x); }
二。小顶堆
使用堆可以求出最小的元素,通过不断地弹出元素,就可以找到第K大的元素
#include <bits/stdc++.h> using namespace std; const int maxn = 1000; int a[maxn]; int n,k; void adjust_heap(int rt,int n) { int x=a[rt],pos = rt; while(rt <= n){ int lc = rt << 1,rc = lc|1; if(lc <= n&&a[lc] < x) pos = lc; if(rc <= n&&a[rc] < a[pos]) pos = rc; if(pos == rt) break; a[rt] = a[pos]; rt = pos; } a[pos] = x; } int search_k() { for(int i = n/2;i >= 1; --i){//建堆的复杂度是O(n)的 adjust_heap(i,n); } int sz = n; for(int i = 1; i < k; ++i){//每次弹出要向下调整,调整K次,复杂度为O(Klogn) a[1] = a[sz--]; adjust_heap(1,sz); } return a[1]; } int main() { srand((unsigned)time(NULL)); scanf("%d%d",&n,&k); for(int i = 1; i <= n; ++i){ a[i] = rand()%(n<<1); } for(int i = 1; i <= n; ++i) printf("%d ",a[i]); printf("\n"); int x = search_k(); printf("%d\n", x); }
相关文章推荐
- Android Animation动画(Frame-By-Frame Animations 、Tween Animations 、Property Animation)的详解
- 2016SDAU编程练习三1017
- Codeforces Round #353 (Div. 2) B. Restoring Painting __ map or set 、思维题
- hdu 2145(迪杰斯特拉)
- BZOJ4556: [Tjoi2016&Heoi2016]字符串
- 1.C++--关于文件间共享代码的方法
- 删除list中元素
- 惊心动魄-微信抢红包传播活动
- iOS: 使用CGContextRef,CGPath和UIBezierPath来绘画
- 二分查找算法
- List集合的并集、交集、差集以及源码
- 单行文字两端对齐
- mysql-创建用户报错ERROR 1396 (HY000): Operation CREATE USER failed for 'XXXX'@'XXXX'
- 2016SDAU编程练习三1016
- A. Summer Camp
- Latex插入图片报错问题处理集锦
- java的动态绑定与静态绑定
- DuiLib CreateControl
- 自定义消息
- HDU 4165 Pills (简单dp)