计算序列中第k小的数
作者:jostree 转载请注明出处 http://www.cnblogs.com/jostree/p/4046399.html
使用分治算法,首先选择随机选择轴值pivot,并使的序列中比pivot小的数在pivot左边,比pivot大的数在pivot右边,即快速排序算法中的partition的过程,可以参考:快速排序算法 Quick sort。
进行partition过程后,我们随机选择的轴值为序列的第j个,且其左边有a个数,右边有b个数。
如果j=k,那么说明该轴值就是第k小个数。
如果j>k,说明第k小的数一定在轴值的左边,我们可以递归的查找左侧a个数中的第k大个数。
如果j<k,说明第k小的数一定在轴值的右侧,且其左侧的a+1个数都小于第k小的数,所以我们可以递归的查找右侧b个数中的第k-a-1小的数。
由于每次规模缩小一半,且每次处理的时间为O(n),那么我们可以得到其平均复杂度为:T(n)=T(n/2)+n
根据主定理我们可以得到算法的复杂度为O(n)
代码如下:
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <ctime>
using namespace std;
void swap(int &a, int &b)
{
int tmp = a;
a = b;
b = tmp;
}
int partation(vector<int> &vec, int begin, int end, int pivot)
{
swap(vec[pivot], vec[begin]);
int tmp = vec[begin];
int p = begin, q = end;
while( p < q )
{
while( p < q && vec[q] >= tmp) q--;
if( p < q ) vec[p++] = vec[q];
while( p < q && vec[p] < tmp ) p++;
if( p < q ) vec[q--] = vec[p];
}
vec[p] = tmp;
return p;
}
int FindKMin(vector<int> & vec, int begin, int end, int k)
{
srand(time());
int piovt = rand()%(end-begin+)+begin;
int pos = partation(vec, begin, end, piovt);
if( pos-begin+ == k )
{
return vec[pos];
}
else if( pos-begin+ > k )
{
return FindKMin(vec, begin, pos-, k);
}
else
{
return FindKMin(vec, pos+, end, k-(pos-begin+));
}
}
int main(int argc, char *argv[])
{
int n;
vector<int> vec;
while( cin >> n )
{
vec.clear();
int k;
cin>>k;
int tmp;
for( int i = ; i < n ; i++ )
{
cin>>tmp;
vec.push_back(tmp);
}
cout<<FindKMin(vec, , vec.size()-, k)<<endl;
}
}
该方法的最坏复杂度为$O(n^2)$,最坏情况是第一阶段每次选择的轴都为最大的数,并递归计算前n-1个数的第k小数,直到第k小数为这个序列的最大值为止。第二阶段每次选择的轴为最小的数,直到只剩下第k小数这一个数。那么该复杂度为$O(n^2)$。
我们可以使用另一种方法来替代随机选择轴值,并使得该算法的最坏情况的复杂度也为O(n),选择轴值方法如下:
1. 将输入数组的n个元素划分为$\lfloor n/5 \rfloor$,每组5个元素,至多只有一组由剩下的nmod5个元素组成。
2. 寻找$\lceil n/5 \rceil$个组中每组的中位数,即对其进行排序,从而找到$\lceil n/5 \rceil$个中位数,并对这$\lceil n/5 \rceil$个中位数组成的数组继续递归调用找出其轴值。
使用该方法找到的轴值并不是数组真正的中位数。但是它具有一定的性质,在大于轴值的那些中位数的组且不包括最后个数少于5的那个组中,每组至少有3个数大于轴值。不计算这两个组,大于轴值的元素个数至少为:
\begin{equation} 3(\lceil \frac{1}{2}\lceil \frac{n}{5}\rceil\rceil -2) \geq \frac{3n}{10}-6 \end{equation}
从而时间复杂度为:$T(n) \leq T(\lceil n/5 \rceil ) + T(7n/10+6) + O(n) = O(n)$
选择轴值的代码,需要建立一个类unit来保存第i个数的值和其位置,并且最终返回轴值的位置。main函数包括了数组长度为4-6的数全排列的测试。
代码如下:
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
class unit
{
public:
int x, num;
unit(int xx=, int nn=)
{
x = xx;
num = nn;
}
bool operator < (const unit &a) const
{
return this->x < a.x;
}
};
int getp(vector<unit> vec)
{
if( vec.size() <= )
{
sort(vec.begin(), vec.end());
return vec[vec.size()/].num;
}
vector<unit> small;
unit tmp;
for( int i = ; i < vec.size()-vec.size()% ; i+= )//i is the start index in the team
{
sort(vec.begin()+i, vec.begin()+i+);
// for( int j = 0 ; j < 5 ; j++ )
// {
// cout<<vec[i+j].x<<" ";
// }
// cout<<endl;
tmp.x = vec[i+].x;
tmp.num = vec[i+].num;
small.push_back(tmp);
}
int remain = vec.size()%;
int teamnum = vec.size()/;
if( remain != )
{
sort(vec.begin()+teamnum*, vec.end());
tmp.x = vec[teamnum*+remain/].x;
tmp.num = vec[teamnum*+remain/].num;
small.push_back(tmp);
}
// for( int i = 0 ; i < small.size() ; i++ )
// {
// cout<<small[i].x<<" ";
// }
// cout<<endl;
return getp(small);
}
int getpivot(const vector<int> vec)
{
vector<unit> vecunit;
unit tmp;
for( int i = ; i < vec.size() ; i++ )
{
tmp.x = vec[i];
tmp.num = i;
vecunit.push_back(tmp);
}
return getp(vecunit);
}
int main(int argc, char *argv[])
{
vector<int> a;
for( int i = ; i < ; i++ )
{
a.clear();
for( int j = ; j < i ; j++ )
{
a.push_back(j);
}
cout<<endl;
do{
cout<<"array is ";
for( int k = ; k < a.size() ; k++ )
{
cout<<a[k]<<" ";
}
cout<<endl;
cout<<"result "<<getpivot(a)<<endl;
}while(next_permutation(a.begin(), a.end()));
}
}
计算序列中第k小的数的更多相关文章
- 顺序统计:寻找序列中第k小的数
最直观的解法,排序之后取下标为k的值即可. 但是此处采取的方法为类似快速排序分块的方法,利用一个支点将序列分为两个子序列(支点左边的值小于支点的值,支点右边大于等于支点的值). 如果支点下标等于k,则 ...
- #7 找出数组中第k小的数
「HW面试题」 [题目] 给定一个整数数组,如何快速地求出该数组中第k小的数.假如数组为[4,0,1,0,2,3],那么第三小的元素是1 [题目分析] 这道题涉及整数列表排序问题,直接使用sort方法 ...
- 选择问题(选择数组中第K小的数)
由排序问题可以引申出选择问题,选择问题就是选择并返回数组中第k小的数,如果把数组全部排好序,在返回第k小的数,也能正确返回,但是这无疑做了很多无用功,由上篇博客中提到的快速排序,稍稍修改下就可以以较小 ...
- Leetcode 668.乘法表中第k小的数
乘法表中第k小的数 几乎每一个人都用 乘法表.但是你能在乘法表中快速找到第k小的数字吗? 给定高度m .宽度n 的一张 m * n的乘法表,以及正整数k,你需要返回表中第k 小的数字. 例 1: 输入 ...
- Java实现 LeetCode 668 乘法表中第k小的数(二分)
668. 乘法表中第k小的数 几乎每一个人都用 乘法表.但是你能在乘法表中快速找到第k小的数字吗? 给定高度m .宽度n 的一张 m * n的乘法表,以及正整数k,你需要返回表中第k 小的数字. 例 ...
- ch1_5_2求无序序列中第k小的元素
import java.util.Arrays; import java.util.PriorityQueue; public class ch1_5_2求无序序列中第k小的元素 { public s ...
- 每天一道算法题(32)——输出数组中第k小的数
1.题目 快速输出第K小的数 2.思路 使用快速排序的思想,递归求解.若键值位置i与k相等,返回.若大于k,则在[start,i-1]中寻找第k大的数.若小于k.则在[i+1,end]中寻找第k+st ...
- [Swift]LeetCode668. 乘法表中第k小的数 | Kth Smallest Number in Multiplication Table
Nearly every one have used the Multiplication Table. But could you find out the k-th smallest number ...
- 求一个数组中第K小的数
面试南大夏令营的同学说被问到了这个问题,我的第一反应是建小顶堆,但是据他说用的是快排的方法说是O(n)的时间复杂度, 但是后来经过我的考证,这个算法在最坏的情况下是O(n^2)的,但是使用堆在一般情况 ...
随机推荐
- iOS 关闭自动锁屏
[UIApplication sharedApplication].idleTimerDisabled=YES;// 不自动锁屏 [UIApplication sharedApplication].i ...
- SPOJ - OTOCI LCT
OTOCI Time Limit: 1 Sec Memory Limit: 256 MB 题目连接 http://acm.hust.edu.cn/vjudge/problem/viewProblem. ...
- ME21N/ME22N/ME23N屏幕增强BADI ME_GUI_PO_CUST
最近想更深入学习下classic BADI, 以前没玩过BADI屏幕增强, 所以决定玩一下. 这次的屏幕增强主要用到两个BADI: ME_GUI_PO_CUST和ME_PROCESS_PO_CUST ...
- debian配置简单的vsftp服务器
Ubuntu Linux与Windows系统不同,Ubuntu Linux不会产生无用垃圾文件,但是在升级缓存中,Ubuntu Linux不会自动删除这些文件,今天就来说说这些垃圾文件清理方法. 1 ...
- OAuth 2 Developers Guide--reference
Introduction This is the user guide for the support for OAuth 2.0. For OAuth 1.0, everything is diff ...
- 汽车OBD2诊断程序开发 (原文转载,思路很清晰!)
1.因TL718已经为你建立了物理层.数据链层和部分应用层的协议,所以只要OBD2标准应用层协议文本,ISO15031-5 或 SAE J1979(这两个协议是相同的内容). 2.TL718诊断 ...
- DHCP 服务器
简介: DHCP(Dynamic Host Configuration Protocol,动态主机配置协议)是一个局域网的网络协议,使用UDP协议工作,主要用途:给内部网络或网络服务供应商自动分配 I ...
- rownum与rowId
一.RowNum Rownum是oracle生成结果集时得到的一个伪列, 按照读出行的顺序, 第一条rownum=1, 第二条=2. 对于 Oracle 的 rownum 问题,很多资料都说不支持 ...
- 如何解决linux(ubuntu/CENTOS)中gedit中文乱码的问题
http://jingyan.baidu.com/article/1709ad80a443c54634c4f09c.html 同时按键盘的Alt 和 F2,就可以打开“运行程序”对话框,这个功能类似于 ...
- app测试点
手机上的app分为基于HTML5的app(类似于pc上的b/S应用)和本地app(类似于C/S结构). 所以测试上我们也可以充分吸收web的b/s和c/s测试经验.但是不同于pc上的应用测试,手机上的 ...