从大小为N的数组中选出第k小的数据
2014-06-18 23:16
232 查看
这个是从《编程珠玑(续)》的最后一章看到的,感觉相当的经典,特此mark一下。
话说一拿到这个题目直接想到的是快排之类的O(NlogN)复杂度的算法:即先排好序,再得到。时间复杂度O(NlogN)实际上是每一层的递归对N个数进行了操作,一共是logN层。下面介绍的算法类似于快排,但是和快排又有区别,每一层并不是操作了N个数,而是对其中的部分进行操作(感觉像是类似剪枝的过程),所以降低了时间复杂度。
算法概要:每一次递归对数组的部分(原始数组记为a,low表示此时部分数组的最小下标,high表示此时部分数组的最大下标)进行操作,第一次递归是整个数组。每一次递归都对数组的部分选定一个数(任意选,简单处理可以选第一个数a[low]),然后将a分为两部分,小于a[low]的和大于a[low]的,将a[low]作为边界放在两部分的中间,然后可以得到此时a[low]的下标index。若index==k,结束递归;若index<k,下一次迭代的范围是index+1到high;若index>k,下一次迭代的范围是low到index-1。
分析(平均)时间复杂度:N+N/2+N/4+N/8+......+1<2N,所以时间复杂度为O(N)。
public class Solution {
//返回结果 k代表第k小的数 k从1开始
public int solve(int[] list,int k){
k = k-1;
if( k>=0 && k<=list.length-1 )
return subSolve(list,0,list.length-1,k);
else
return Integer.MIN_VALUE;
}
//返回查找的结果 i是当前的分界线,j是一直增长的
public int subSolve(int[] list,int low,int high,int k){
int i = low+1;
for(int j=low+1;j<=high;){
if( list[j]<list[low] ){
swap(list,i,j);
i++;
j++;
}else{
j++;
}
}
swap(list,low,i-1);
if(i-1==k)
return list[i-1];
else if(i-1<k)
return subSolve(list,i,high,k);
else
return subSolve(list,low,i-2,k);
}
//交换数组的元素
public void swap(int[] list,int a,int b){
int temp = list[a];
list[a] = list[b];
list[b] = temp;
}
public static void main(String[] args){
System.out.println(new Solution().solve(new int[]{2,1,5,4,3,6,9,100,-1,0,-100},6));
}
}
话说一拿到这个题目直接想到的是快排之类的O(NlogN)复杂度的算法:即先排好序,再得到。时间复杂度O(NlogN)实际上是每一层的递归对N个数进行了操作,一共是logN层。下面介绍的算法类似于快排,但是和快排又有区别,每一层并不是操作了N个数,而是对其中的部分进行操作(感觉像是类似剪枝的过程),所以降低了时间复杂度。
算法概要:每一次递归对数组的部分(原始数组记为a,low表示此时部分数组的最小下标,high表示此时部分数组的最大下标)进行操作,第一次递归是整个数组。每一次递归都对数组的部分选定一个数(任意选,简单处理可以选第一个数a[low]),然后将a分为两部分,小于a[low]的和大于a[low]的,将a[low]作为边界放在两部分的中间,然后可以得到此时a[low]的下标index。若index==k,结束递归;若index<k,下一次迭代的范围是index+1到high;若index>k,下一次迭代的范围是low到index-1。
分析(平均)时间复杂度:N+N/2+N/4+N/8+......+1<2N,所以时间复杂度为O(N)。
public class Solution {
//返回结果 k代表第k小的数 k从1开始
public int solve(int[] list,int k){
k = k-1;
if( k>=0 && k<=list.length-1 )
return subSolve(list,0,list.length-1,k);
else
return Integer.MIN_VALUE;
}
//返回查找的结果 i是当前的分界线,j是一直增长的
public int subSolve(int[] list,int low,int high,int k){
int i = low+1;
for(int j=low+1;j<=high;){
if( list[j]<list[low] ){
swap(list,i,j);
i++;
j++;
}else{
j++;
}
}
swap(list,low,i-1);
if(i-1==k)
return list[i-1];
else if(i-1<k)
return subSolve(list,i,high,k);
else
return subSolve(list,low,i-2,k);
}
//交换数组的元素
public void swap(int[] list,int a,int b){
int temp = list[a];
list[a] = list[b];
list[b] = temp;
}
public static void main(String[] args){
System.out.println(new Solution().solve(new int[]{2,1,5,4,3,6,9,100,-1,0,-100},6));
}
}
相关文章推荐
- MATLAB访问cell数组的数据(大小括号的区别)
- 求无序数组第K大小的数值
- 有一个整数数组,请你根据快速排序的思路,找出数组中第K大的数。 给定一个整数数组a,同时给定它的大小n和要找的K(K在1到n之间),请返回第K大的数,保证答案存在。
- 假设一个大小为100亿个数据的数组,该数组是从小到大排好序的,现在该数组分成若干段,每个段的数据长度小于20「也就是说:题目并没有说每段数据的size 相同,只是说每个段的 size < 20 而已」
- 线性时间O(n)内求数组中第k大小的数
- 面试题 M大小的数组中选出前N个元素
- 对数组中数据的大小排序
- 从大小为n的数组中随机选出m个整数,要求被选中的概率相同
- 建立一个数组,从五个数据选出三个(第三章第四题)
- 程序员面试金典——解题总结: 9.18高难度题 18.3编写一个方法,从大小为n的数组中随机选出m个整数。要求每个元素被选中的概率相同。
- 写一段程序,找出数组中第k大小的数,输出数所在的位置。
- 2 个按升序(从小到大)排列的数列 A[1],A[2],....,A[M]和 B[1],B[2],...,B[N]中各元素按其大小关系存放到数组 C。同时必须注意对数组 A 或 B 残余元素的追加处理。(数组 A、B 中初始元素数量自定,数据从键盘输入。)
- 编写程序从标准输入设备读入的元素数据建立一个int型vector对象,然后动态创建与该vector大小一致的数组,把vector对象的所有元素复制给该数组
- 指定数组中的数据比大小
- 柔性数组,数据类型大小
- 找出数组中第k大小的数,输出数所在的位置
- c复习作业: 从键盘接受一个数组 数组的大小 随着输入数据的变化 而变化
- MATLAB访问cell数组的数据(大小括号的区别)
- 从整数1到99之间选出能被3整除、且有一位上的数是5的那些数,并把它们放在p数组中,输出(p数组中)满足条件的数据。
- 算法随机在一定范围内(min-max)选出m个不重复的数据放到一个数组中