第k小的数
来源:互联网 发布:勒布朗詹姆斯 知乎 编辑:程序博客网 时间:2024/05/21 07:07
输入n个整数和一个正整数k(1<=k<=n),输出这些整数从小到大排序后的第k个
思路1:最容易想到的方法:先对这个序列从小到大排序,然后输出前面的最小的k个数即可。如果选择快速排序法来进行排序,则时间复杂度:O(n*logn)
class Solution //时间复杂度O(NlogN){ public: int numkth(vector<int>& number,int k) { sort(number.begin(),number.end()); return number[k-1]; }};
思路2:在思路1的基础上更进一步想想,题目并没有要求要查找的k个数,甚至后n-k个数是有序的,既然如此,咱们又何必对所有的n个数都进行排序列?如此,我们能想打的一个方法是:遍历n个数,先把最先遍历到得k个数存入大小为k的数组之中,对这k个数,利用选择或交换排序,找到k个数中的最大数kmax(kmax设为k个元素的数组中最大元素),用时O(k)(你应该知道,插入或选择排序查找操作需要O(k)的时间),后再继续遍历后n-k个数,x与kmax比较:如果x< kmax,则x代替kmax,并再次重新找出k个元素的数组中最大元素kmax‘;如果x>kmax,则不更新数组。这样,每次更新或不更新数组的所用的时间为O(k)或O(0),整趟下来,总的时间复杂度平均下来为:n*O(k)=O(n*k)
class Solution{ public: int numkth(vector<int>& number,int k)//时间复杂度O(N*k) { int *num=new int[k]; for(int i=0;i<k;i++) num[i]=number[i]; for(int j=k;j<number.size()-k;j++) { if(number[j]<num[indexkth(num,k)]) num[indexkth(num,k)]=number[j]; } int result=num[indexkth(num,k)]; delete []num; return result; } int indexkth(int num[],int k)//找到k个数中最大值的索引下标O(k) { int index=0; int max=num[index]; for(int i=1;i<k;i++) { if(num[i]>max) { index=i; max=num[i]; } } return index; }};
思路3:与思路2方法类似,只是用容量为k的最大堆取代思路2中数组的作用(从数组中找最大数需要O(k)次查找,而从更新一个堆使之成为最大堆只需要O(logk)次操作)。具体做法如下:用容量为k的最大堆存储最先遍历到的k个数,并假设它们即是最小的k个数,建堆费时O(k)后,有k1< k2< …< kmax(kmax设为大顶堆中最大元素)。继续遍历数列,每次遍历一个元素x,与堆顶元素比较,x< kmax,更新堆(用时logk),否则不更新堆。这样下来,总费时O(k+(n-k)*logk)=O(n*logk)。
class Solution{ public: int numkth(vector<int>& number,int k)//总费时O(k+(n-k)*logk)=O(n*logk) { int *num=new int[k],i; for(i=0;i<k;i++) num[i]=number[i]; for(i=(k-1)/2;i>=0;i--)//建堆O(k) Adjust(num,i,k); for(i=k;i<number.size();i++) { if(number[i]<num[0]) { num[0]=number[i]; Adjust(num,0,k); } } return num[0]; } void Adjust(int A[],int i,int N)//时间复杂度O(logN) { int temp,child; for(temp=A[i];2*i+1<N;i=child) { child=2*i+1; if((child!=N-1)&&A[child+1]>A[child]) child++; if(temp<A[child]) A[i]=A[child]; else break; } A[i]=temp; }};
思路4:按编程之美中给出的描述,类似快速排序的划分方法,N个数存储在数组S中,再从数组中随机选取一个数X(随机选取枢纽元,可做到线性期望时间O(N)的复杂度),把数组划分为Sa和Sb俩部分,Sa<=X<=Sb,如果要查找的k个元素小于Sa的元素个数,则返回Sa中较小的k个元素,否则返回Sa中所有元素+Sb中小的k-|Sa|个元素。像上述过程一样,这个运用类似快速排序的partition的快速选择SELECT算法寻找最小的k个元素,在最坏情况下亦能做到O(N)的复杂度。
class Solution{ public: int numkth(vector<int>& number,int k)//分治法,时间复杂度为O(N) { int num=number.size(); int *A=new int[num]; for(int i=0;i<num;i++) A[i]=number[i]; int result=helper(A,0,num-1,k); delete []A; return result; } int helper(int A[],int low,int high,int k) { int pivot=A[low]; int L=low,R=high; 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; if(low==k-1) return A[low]; else if(low>k-1) return helper(A,L,low-1,k); else return helper(A,low+1,R,k); }};
思路5:仍然用到数据结构:堆。具体做法为:针对整个数组序列建最小堆,建堆所用时间为O(n),然后取堆中的前k个数,总的时间复杂度即为:O(n+k*logn)。
class Solution{ public: int numkth(vector<int>& number,int k)//总费时O(k+(n-k)*logk)=O(n*logk) { int length=number.size(); int *num=new int[length],i,temp; for(i=0;i<length;i++) num[i]=number[i]; for(i=(length-1)/2;i>=0;i--)//建最小堆O(N) Adjust(num,i,N); for(i=length-1;i>=length-k;i--) { temp=num[0];num[0]=num[i];num[i]=temp; Adjust(num,0,i); } return num[length-k]; } void Adjust(int A[],int i,int N)//时间复杂度O(logN) { int temp,child; for(temp=A[i];2*i+1<N;i=child) { child=2*i+1; if((child!=N-1)&&A[child+1]<A[child]) child++; if(temp>A[child]) A[i]=A[child]; else break; } A[i]=temp; }};
- 第k小的数
- 第k小的数
- rqnoj350 第k小的数
- 寻找第K小的数
- 【模拟】找第k小的数
- 寻找第K小的数
- 算法--选择第K小的数
- 寻找第K小的数
- 第k个小的数
- 求第k小的数
- Java 求第K小的数
- 并行计算第k小的数
- 数组中第k小的数
- 寻找第K小的数
- 求第k小的数
- 巴蜀1322 第k小的数
- 分治-寻找第k小的数
- 数组第K小的数
- Hive知识点二(数据操作)
- hibernate中的配置文件详解
- 类的高级概念:继承
- 笨小猴
- Modbus协议和CAN总线
- 第k小的数
- 多物体运动
- [Leetcode] Move Zeroes
- jQuery 选择器分类
- 「iOS 100天学习计划」 第十二天~第十三天
- 中秋NOIP模拟赛总结
- Codeforces Round #371 (Div. 2) E 【DP+离散化 LIS 】用最小代价把序列变成严格递增序列
- 53. Maximum Subarray 难度:medium 类别:分治
- 运算符和函数区别