快速选择算法,是一种能在大致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. [已解决]gitee初次使用git clone报错

    本文描述的错误按实际出现先后顺序排列,并且附上一些其他可能会出现的问题 错误1: JZKJ@DESKTOP-I7Q9QJ4 MINGW64 ~ $ git clone https://gitee.co ...

  2. C-基础:详解sizeof和strlen,以及strstr

    sizeof和strlen (string.h) 先看几个例子(sizeof和strlen之间的区别):  (1) 对于一个指针, char* ss ="0123456789"; ...

  3. 第2节 azkaban调度:16、azkaban的介绍以及azkaban的soloserver的安装使用

    2. 工作流调度器azkaban 2.1 概述 azkaban官网: https://azkaban.github.io/ 2.1.1为什么需要工作流调度系统 l  一个完整的数据分析系统通常都是由大 ...

  4. fossil 代理设置

    C:\>fossil user new Joe C:\>fossil user default Joe 设置账户 fossil setting proxy http://-:-fossil ...

  5. SpringBoot整合升级Spring Security 报错 【The request was rejected because the URL was not normalized】

    前言 最近LZ给项目框架升级, 从Spring1.x升级到Spring2.x, 在这里就不多赘述两个版本之间的区别以及升级的原因. 关于升级过程中踩的坑,在其他博文中会做比较详细的记录,以便给读者参考 ...

  6. java socket domain name 使用域名.

    java 的 socket 依赖了 nameService.  引擎模式. 使得 socket tcp 层 具有了上层业务的能力 (应用层) Socket socket=new Socket(&quo ...

  7. 当数据量很少的时候,tableview会显示多余的cell--iOS开发系列---项目中成长的知识二

    当数据量很少的时候,tableview会显示很多的cell,而且是空白的,这样很不美观 所以使用下面的方法可以去掉多余的底部的cell 原理是:设置footerView为frame 是 CGRectZ ...

  8. baidumap demo(二)

    接口说明 百度地图API提供的搜索服务包括:POI检索,多关键字检索,公交方案检索,驾车路线检索,步行路线检索,地理编码,反地理编码,公交详情检索,在线建议查询,短串分享. 所有检索请求接口均为异步接 ...

  9. ios之UIPopoverController

    UIPopoverController是iPad上的iOS开发会常用到的一个组件(在iPhone设备上不允许使用),这个组件上手很简单,因为他的显示方法很少,而且参数简单,但我在使用过程中还常碰到各种 ...

  10. ios之自定义UISwitch

    系统自带的UISwitch是这样的: 既不能写字,也不能改颜色,于是在网上找到了这么一个自定义的Switch按钮,具体出处找不见了.记录一下,怕以后找不见了. 先看下效果图: 按钮的样式很多,可以文字 ...