计算序列中第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)的,但是使用堆在一般情况 ...
随机推荐
- 使用Knoctout.分页
要点:点击标签翻页部分时,pageValue自动加1. 1.api中的方法 public ArticleListModel Get(int page,string _class) { var list ...
- 获取WMI硬件清单
WMI服务能够报告详细的硬件信息.通常,每个硬件都来自它们自己的WMI代理类.但是要找出这些硬件类的名字是不容易. 所有硬件类都在同一个WMI根下面,你可以在根类查询所有的硬件: Get-WmiObj ...
- 从零开始学android开发-查看sqlite数据库
C:\Users\Administrator>cd E:\ProSoft\adt-bundle-windows-x86-20140321\sdk\platform-tools
- 【异构计算】OpenCL矩阵转置
介绍 矩阵转置,主要的技巧还是利用好local memory ,防止local memory,以及glabol memory的读取尽量是合并读写. 完整代码一: main.cpp代码 #include ...
- 信号之kill和raise函数
kill函数将信号发送给进程或进程组.raise函数则允许进程向自身发送信号. #include <signal.h> int kill(pid_t pid, int signo); in ...
- 类 ArrayBlockingQueue<E>(一个由数组支持的有界阻塞队列。)
类型参数: E - 在此 collection 中保持的元素类型 所有已实现的接口: Serializable, Iterable<E>, Collection<E>, Blo ...
- Asp.Net 之 缓存机制
asp.net缓存有三种:页面缓存,数据源缓存,数据缓存. 一.页面缓存 原理:页面缓存是最常用的缓存方式,原理是用户第一次访问的时候asp.net服务器把动态生成的页面存到内存里,之后一段时间再有用 ...
- SVN服务器搭建和使用(二)
上一篇介绍了VisualSVN Server和TortoiseSVN的下载,安装,汉化.这篇介绍一下如何使用VisualSVN Server建立版本库,以及TortoiseSVN的使用. 首先打开 ...
- C语言结构体赋值2
#include <stdio.h> /** 上一个版本的name是固定大小的,不好,这次换用 *name然后 采用 堆的方式申请内存,起到用到少拿多少的一个方式. */ struct s ...
- 深入理解计算机系统第二版习题解答CSAPP 2.17
假设w=4,我们能给每个可能的十六进制数字赋予一个数值,假设用一个无符号或者补码表示.完成下表: x 无符号(B2U(x)) 补码(B2T(x)) 十六进制 二进制 0xE 1110 14 -2 0x ...