(转载请注明出处,http://www.cnblogs.com/fangpei/p/3538331.html )

以前写过的一篇,搬过来。

上算法课的时候听到老师讲这个问题,觉得还是蛮有意思的。已知数组A,找出A[m]...A[p]中的第k大值。

很容易想到快排和冒泡。

第一种方法:用快排的分治方法,是先任意找数组中的一个元素a(a用数组的第一个元素比较方便),然后进行一次划分,就是将数组中所有大于a的数都移到a的一边,所有小于等于a的数都移到A的另一边。然后选择在哪边继续进行划分,最后找到第k大的值。

第二种方法:用冒泡的方法,是每个元素挨着比,第一趟找出最大的数,第二趟找出第2大的数,一直到找到第k大的数结束。

其实第一种方法的平均复杂度能到O(n),但是它的复杂度依赖于划分元素,最坏的时间复杂度是O(n^2)。

      如果在第一种方法之上,加上一个筛选划分元素的过程,就能把最坏时间复杂度降到O(n)。筛选的过程就是把所有的数等分成很多小段,然后求所有小段的中间值。构成一个由所有中间值组成的段,然后再取中间值,作为划分元素。即中间值的中间值作为划分元素。取中间值可以先任选一种排序方法排序之后选择,因为每一小段的长度很短,不是影响复杂度的主要因素;取中间值的中间值,利用递归的方法调用自身即可。

这样就可以把最坏时间复杂度降到O(n)了,复杂度证明比较繁琐。

用C++实现了一下:

#include<iostream>
using namespace std; int r = ; //定义全局变量r, r个元素一段 void InSort( int A[], int m, int p ) //插入排序
{
int i;
for( i = m + ; i <= p; ++i ) {
int t;
t = A[i];
int j;
for( j = i - ; j >= m; --j ) {
if( t < A[j] )
A[j+] = A[j];
else
break;
}
A[j+] = t;
}
} void Swap( int &a, int &b ) //两数交换
{
int temp = ;
temp = a;
a = b;
b = temp;
} int Partition( int A[], int m, int p ) //一次划分函数
{
int i = m, j = p + ;
int x = A[m];
while( ) {
while( A[++i] > x );
while( A[--j] < x );
if( i >= j)
break;
Swap( A[i], A[j] );
}
A[m] = A[j];
A[j] = x;
return j;
} int Select( int A[], int m, int p, int k ) //返回一个i值,使得A[i]是A[m..p]中第k小元素
{
int n = , i = , j = ;
if( p - m + <= r ) {
InSort( A, m, p );
return m + k - ;
}
while( ) {
n = p - m + ;
for ( i = ; i <= int(n/r); ++i ) { //计算中间值
InSort( A, m + (i - ) * r, m + i * r - );
//将中间值收集到A[m..p]的前部
Swap( A[m+i-], A[m+(i-)*r+int(r/)] );
}
j = Select( A, m, m + int(n/r) -, int(int(n/r)/) + );
Swap( A[m], A[j] ); //产生划分元素
j = Partition( A, m, p );
if( j - m + == k)
return j;
else if( j - m + > k )
p = j - ;
else {
k = k - ( j - m + );
m = j + ;
}
}
} int main()
{
int A[] = { , , , , , , , , , , , , , , , , , , , , , , , };
int find_out = Select( A, , , );
int i;
for( i = ; i <= ; ++i )
cout << A[i] <<" ";
cout << endl;
cout << A[find_out] << endl;
return ;
}

另外:
1、上面说的都是在内存够用的前提下。
2、调这个程序的时候发现了一个问题:

以前我以为下面这样交换两个数比较好。
void Swap( int &a, int &b )
{
a = a ^ b;
b = a ^ b;
a = a ^ b;
}

才发现如果a和b表示同一个地址的时候,就是错的(不管是什么都变成0了)。

所以如果可能出现a、b是同一个地址上的数的时候,为了避免有隐藏的bug,还是下面这样保险。(虽然多了一个临时变量的空间)
void Swap( int &a, int &b )
{
int temp = ;
temp = a;
a = b;
b = temp;
}

找第k大数,最坏时间复杂度O(n)的更多相关文章

  1. 牛客网-3 网易编程题(1拓扑&2二叉树的公共最近祖先&3快排找第K大数)

    1. 小明陪小红去看钻石,他们从一堆钻石中随机抽取两颗并比较她们的重量.这些钻石的重量各不相同.在他们们比较了一段时间后,它们看中了两颗钻石g1和g2.现在请你根据之前比较的信息判断这两颗钻石的哪颗更 ...

  2. [剑指Offer]39-数组中出现次数超过一半的数字(快排延申,找第k大数同理)

    题目链接 https://www.nowcoder.com/practice/e8a1b01a2df14cb2b228b30ee6a92163?tpId=13&tqId=11181&t ...

  3. 从一组数找第K大元素

    最近做面试题,经常与到一个问题,如何高效的从一组数中找到第K大的元素. 其实我们最容易想到的肯定是蛮力法. 1. 我们可以对这个乱序数组按照从大到小先行排序,然后取出前k大,总的时间复杂度为O(n*l ...

  4. 杨氏矩阵:查找x是否在矩阵中,第K大数

    参考:http://xudacheng06.blog.163.com/blog/static/4894143320127891610158/ 杨氏矩阵(Young Tableau)是一个很奇妙的数据结 ...

  5. 第k大数问题

    解法1: 我们可以对这个乱序数组按照从大到小先行排序,然后取出前k大,总的时间复杂度为O(n*logn + k). 解法2: 利用选择排序或交互排序,K次选择后即可得到第k大的数.总的时间复杂度为O( ...

  6. 算法打基础——顺序统计(找第k小数)

    这次主要是讲如何在线性时间下找n个元素的未排序序列中第k小的数.当然如果\(k=1 or k=n\),即找最大最小 数,线性时间内遍历即可完成,当拓展到一般,如中位数时,相关算法就值得研究了.这里还要 ...

  7. 第k大数(前k大数)

    题目:设计一组N个数,确定其中第k个最大值 1,普通方法(先排序,然后遍历,得到第k大的数)      注:如果是数组,直接arr[k],我们可以对这个乱序数组按照从大到小先行排序,然后取出前k大,总 ...

  8. POJ 2104 K-th Number(区间第k大数)(平方切割,归并树,划分树)

    题目链接: http://poj.org/problem? id=2104 解题思路: 由于查询的个数m非常大.朴素的求法无法在规定时间内求解. 因此应该选用合理的方式维护数据来做到高效地查询. 假设 ...

  9. BFPRT: O(n)最坏时间复杂度找第K大问题

    同时找到最大值与最小值 找到n个元素中的最大/小值,比较次数为n-1, 找到n个元素中的最大值和最小值,可以Two Pass,比较次数为2n-2 也可以One Pass,比较次数至多为\(\left ...

随机推荐

  1. Android ActionBar(转)

    本文内容 关于 ActionBar 必要条件 项目结构 环境 演示一:Action Bar 显示隐藏 演示二:Action Item 显示菜单选项 演示三:Action Home 启用“返回/向上”程 ...

  2. 一道在知乎很火的 Java 题——如何输出 ab【转】

    这是一个源自知乎的话题,原贴链接:一道百度的面试题,有大神会嘛? 虽然我不是大神,但我也点进去看了一下,思考了一会之后有了一些思路,然后去看其它人的答案的时候果然全都已经被各路大神们先想到并贴出来了, ...

  3. Datables wrning(table id='example'):Cannot reinitialise DataTable.

    出现的问题如下所示: Datables wrning(table id='example' Datables object for this table,please pass eithser no ...

  4. 从一个SVN下载的导入另一个SVN里面

    如果项目是你从个一个SVN下载的,你想存入另一个SVN里面 那么问题来了 你用eclipse的team的时候会发现没有Team share 这个选项,那么就等于是没有上传的选项了 解决办法,把项目删掉 ...

  5. show_space.sql.txt

    create or replace procedure SHOW_SPACE(P_SEGNAME   IN VARCHAR2, P_OWNER     IN VARCHAR2 DEFAULT USER ...

  6. [转] iOS开发- UICollectionView详解+实例

    本章通过先总体介绍UICollectionView及其常用方法,再结合一个实例,了解如何使用UICollectionView. UICollectionView 和 UICollectionViewC ...

  7. 微信返回上一页,页面中的AJAX的请求,对Get请求无效的解决办法

    问题产生原因       最近在做一个微信的项目时,遇到一种很常见的情况,需求是这样的,当用户进入到"我的个人中心"的时候,会有一个点击跳转填写认证资料的按钮,点击此按钮后,会跳转 ...

  8. AMD和CMD的区别

    1.cmd define(function(require,export){ var b = 1; var a = require('./a'); a.dosomething(); }); 2.amd ...

  9. 最短路径算法——Dijkstra,Bellman-Ford,Floyd-Warshall,Johnson

    根据DSqiu的blog整理出来 :http://dsqiu.iteye.com/blog/1689163 PS:模板是自己写的,如有错误欢迎指出~ 本文内容框架: §1 Dijkstra算法 §2 ...

  10. Log4j实现对Java日志的配置全攻略

    1. 配置文件 Log4J配置文件的基本格式如下: #配置根Logger log4j.rootLogger = [ level ] , appenderName1 , appenderName2 , ...