快速选择算法,是一种能在大致O(N)的时间内选取数组中第k大或者k小的算法.其基本思路与快速排序算法类似,也是分治的思想.

其实这个算法是个基础算法,但是不常用,所以今天编的时候错了POJ2388,才有了这篇文章.

  1. 执行Partition算法(就是那个快排里将区间内所有数划分为小的一部分和大的一部分的过程)
  2. 判断第k大的数是在小的部分还是大的部分
  3. 递归,直到区间足够小,返回结果

下面几段代码,尤其要注意的是

while(i<j)

还是

while(i<=j)

程序1:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/*
Program:快速选择算法样例
Author:Comzyh
*/
#include <cstdio>
int array[10000],temp;
int N,K;
int QuickSelect(int arr[],int b,int e,int k);
int main()
{
     scanf("%d%d",N,K);
     for (int i=1;i<=N;i++)
          scanf("%d",array[i]);
     printf("The k th :%d\n",QuickSelect(array,1,N,K));
}
int QuickSelect(int arr[],int b,int e,int k)
{
     int i=b,j=e,mid=arr[(i+j)>>1];
     while (i<=j)//注意,小于等于
     {
          while (arr[i]<mid)i++;
          while (arr[j]>mid)j--;
          if (i<=j)
          {
               temp=arr[i];arr[i]=arr[j];arr[j]=temp;
               i++;j--;
          }
     }
     if (b<j  k<=j)return QuickSelect(arr,b,j,k);//分治
     if (i<e  k>=i)return QuickSelect(arr,i,e,k);
     return arr[k];//如果不属于任何一方,就结束,返回
}

不过,就是这样一个简单的算法,今天也出了点错误,本来我是用用了多少年的快排改的,就像下面这段代码

程序2:

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/*
Program:快速排序算法样例
Author:Comzyh
*/
#include <cstdio>
int array[10000],temp;
int N,K;
int QuickSort(int arr[],int b,int e);
int main()
{
     scanf("%d",N);
     for (int i=1;i<=N;i++)
          scanf("%d",array[i]);
     QuickSort(array,1,N);
     for (int i=1;i<=N;i++)
          printf("%d\n",array[i]);
}
int QuickSort(int arr[],int b,int e)
{
     int i=b,j=e,mid=arr[(i+j)>>1];
     while (i<j)//注意,小于
     {
          while (arr[i]<mid)i++;
          while (arr[j]>mid)j--;
          if (i<=j)
          {
               temp=arr[i];arr[i]=arr[j];arr[j]=temp;
               i++;j--;
          }
     }
     if (b<j)QuickSort(arr,b,j);
     if (i<e)QuickSort(arr,i,e);
}

几乎一模一样,但是下面这样写就是是错的

程序3:

 
1
2
3
4
5
6
7
8
9
10
11
int QuickSelect(int arr[],int b,int e,int k)
{
     int i=b,j=e,mid=arr[(i+j)>>1];
     while (i<j)//注意,小于
     {
          ....
     }
     if (b<j  k<=j)return QuickSelect(arr,b,j,k);//分治
     if (i<e  k>=i)return QuickSelect(arr,i,e,k);
     return arr[k];//如果不属于任何一方,就结束,返回
}

而这样写是对的

程序4:

 
1
2
3
4
5
6
7
8
9
10
11
int QuickSelect(int arr[],int b,int e,int k)
{
     int i=b,j=e,mid=arr[(i+j)>>1];
     while (i<j)//注意,小于
     {
          ....
     }
     if (b<j  k<=j)QuickSelect(arr,b,j,k);//没有Return
     if (i<e  k>=i) QuickSelect(arr,i,e,k);
     return arr[k];//如果不属于任何一方,就结束,返回
}
快速排序已经模板化了,原理也清楚,实现也正确,但是,有些细节有可能理解不到位,所以才会出错.
下面分析这种情况出现的原因:
出错其实是一种极端情况,即向右扫描的指针i和向左扫描的指针j重合于k位置;.(这种巧合真的不大常见,但是还是让我给碰上了,如果没碰上,估计我的错误也不会被纠正.)
 
假设有一个数组arr[]={1,4,3,6,3,2},当k=4时;(下标从1算起,下同)
快速选择算法细节演示
首先,按照Partition算法,先交换arr[2]=4 和arr[6]=2,变成arr[]={1,2,3,6,3,4}
然后i=3,j=5 如图(1)交换,i++,j–后,i=j=k=4 如图(2)
  • 按照错误的方法(程序3)执行(如图(3)),函数会在闭区间[1,4]中寻找答案,这样是错误的,因为arr[5]=3不在这个区间内
  • 按照程序1中的方法执行,j会自减1,因为不满足i<=j(i=4,j=3)然后会在闭区间[4,6]中递归(如图(4)),寻找答案,这样是正确的
  • 按照程序4中的方法执行,QuickSelect(1,4,4)执行完之后arr[4]=6,这样,再执行QuickSelect(4,6,4)时,程序会返回正确的结果

选取第K大数的快速选择算法和注意事项的更多相关文章

  1. 蓝桥杯 算法训练 区间k大数查询(水题)

    算法训练 区间k大数查询 时间限制:1.0s   内存限制:256.0MB 问题描述 给定一个序列,每次询问序列中第l个数到第r个数中第K大的数是哪个. 输入格式 第一行包含一个数n,表示序列长度. ...

  2. 算法训练 区间k大数查询

    http://lx.lanqiao.org/problem.page?gpid=T11 算法训练 区间k大数查询   时间限制:1.0s   内存限制:256.0MB        问题描述 给定一个 ...

  3. POJ 2388 Who's in the Middle (快速选择算法:O(N)求数列第K大)

    [题意]求数列中间项. ---这里可以扩展到数列第K项. 第一次做的时候直接排序水过了= =--这一次回头来学O(N)的快速选择算法. 快速选择算法基于快速排序的过程,每个阶段我们选择一个数为基准,并 ...

  4. 蓝桥杯--算法训练 区间k大数查询

                                                                                 算法训练 区间k大数查询   时间限制:1.0 ...

  5. 普林斯顿大学算法课 Algorithm Part I Week 3 求第K大数 Selection

    问题 给定N个元素的数组,求第k大的数. 特例当k=0时,就是求最大值,当k=N-1时,就是求最小值. 应用顺序统计求top N排行榜 基本思想 使用快速排序方法中的分区思想,使得a[k]左侧没有更小 ...

  6. 算法训练 区间K大数

    算法训练 区间k大数查询 时间限制:1.0s   内存限制:256.0MB 问题描述 给定一个序列,每次询问序列中第l个数到第r个数中第K大的数是哪个. 输入格式 第一行包含一个数n,表示序列长度. ...

  7. 分治算法--寻找第k大数

    问题描述:给定线性序集中n个元素和一个整数k,1≤k≤n,要求找出这n个元素中第k大的元素,(这里给定的线性集是无序的). 其实这个问题很简单,直接对线性序列集qsort,再找出第k个即可.但是这样的 ...

  8. 蓝桥杯算法训练 区间k大数查询

    算法训练 区间k大数查询   问题描述 给定一个序列,每次询问序列中第l个数到第r个数中第K大的数是哪个. 输入格式 第一行包含一个数n,表示序列长度. 第二行包含n个正整数,表示给定的序列. 第三个 ...

  9. Java实现 蓝桥杯 算法训练 区间k大数

    算法训练 区间k大数查询 时间限制:1.0s 内存限制:256.0MB 问题描述 给定一个序列,每次询问序列中第l个数到第r个数中第K大的数是哪个. 输入格式 第一行包含一个数n,表示序列长度. 第二 ...

随机推荐

  1. python_113_socket编程

    Socket语法及相关 socket概念 socket本质上就是在2台网络互通的电脑之间,架设一个通道,两台电脑通过这个通道来实现数据的互相传递. 我们知道网络 通信 都 是基于 ip+port 方能 ...

  2. 解决IllegalBlockSizeException:last block incomplete in decryption异常

    解决IllegalBlockSizeException:last block incomplete in decryption异常分类: webkit android最近做个加解密的实现,虽然实现了, ...

  3. Bootstrap历练实例:超小的按钮

    <!DOCTYPE html><html><head> <meta http-equiv="Content-Type" content=& ...

  4. mac拷贝原版和权限修复的命令行工具

    建议直接从安装盘中用命令复制,因为上传的kext权限会变,导致签名失败. 假定安装盘盘符是install_osx: sudo cp -R /Volumes/install_osx/S*/L*/E*/A ...

  5. dubbo---------timeout与retires

    相信很多人都见过这张图,这张图说明了提供者与消费者之间的关系,下面就介绍一下这个图是什么意思. 1.角色解释: Provider: 暴露服务的服务提供者. Consumer: 调用远程服务的服务消费者 ...

  6. (38)zabbix中配置snmp监控

    1.首先按照“snmp监控快速配置”文本文档在被监控的主机上安装.配置及启动snmp服务, 具体内容如下: 1).安装snmp yum install net-snmp* -y cp -a /etc/ ...

  7. 杭电 1503 Advanced Fruits

    Description The company "21st Century Fruits" has specialized in creating new sorts of fru ...

  8. 如何在eclipse中引用第三方jar包

    在用UiAutomator做手机自动化测试过程中,在UiAutomator的基础之上进一步封装了里边的方法,以使case开发更顺手.直接在工程的根目录下新建了个libs的文件夹,把封装好的框架打成ja ...

  9. PYDay2-linux基础\常用命令

    一.linux 理念 一切皆文件 二.常用命令(150) 2.1.rsync rsync是类unix系统下的数据镜像备份工具, 它的特性如下: 可以镜像保存整个目录树和文件系统. 可以很容易做到保持原 ...

  10. luogu3159 [CQOI2012]交换棋子

    把每个点拆成 x y z 对于第 i 个点,x->y是表示流入的,y->z是表示流出的. #include <iostream> #include <cstring> ...