快速选择算法,是一种能在大致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. 用 Deployment 运行应用【转】

    从本章开始,我们将通过实践深入学习 Kubernetes 的各种特性.作为容器编排引擎,最重要也是最基本的功能当然是运行容器化应用,这就是本章的内容. Deployment 前面我们已经了解到,Kub ...

  2. abp viewmodel的写法

    我的写法 public class QuotaCreateOrEditViewModel { public QuotaDto LoanQuota { get; set; } public bool I ...

  3. Base64编码密钥时关于换行的几个问题。

    在windows下一个javaweb应用,需要用http传递公钥pk.一般是String pk = BASE64ENCODER.encode(pkBytes);base64编码时,每76个字母就要换行 ...

  4. thinkphp 结合phpexcel实现excel导入

    控制器文件: class ExcelAction extends Action { public function __construct() { import('ORG.Util.ExcelToAr ...

  5. 使用CSS来制作类似「黑幕」的效果

    网上几乎没有看到这类的代码,留个档 .heimu,.heimu a{ background-color: #252525; color:#252525; text-shadow: none; }::s ...

  6. 五:SQL语句中的数据类型

    一:MySQL数据类型 MySQL中定义数据字段的类型对你数据库的优化是非常重要的 MySQL支持多种数据类型,大致可以分为三类:数值 日期/时间和字符串 二.数值类型(12) 2.1.整数类型(6) ...

  7. JS中关于clientWidth offsetWidth scrollWidth 等的含义的详细介绍

    网页可见区域宽: document.body.clientWidth;网页可见区域高: document.body.clientHeight;网页可见区域宽: document.body.offset ...

  8. django第9天(多表操作)

    django第9天 models类 class Book(Model): id = AutoField(primary_key=True) name = CharField(max_length=20 ...

  9. leetcode-1-basic

    leetcode-algorithm 1. Two Sum 解法:循环,试呗..简单粗暴.. class Solution { public: vector<int> twoSum(vec ...

  10. 数据结构之--图(Graphics)

    1.1:图的定义和术语   图是一种比线性表和树更为复杂的数据结构.在线性表中,数据元素之间仅有线性关系,每个元素仅有一个直接前驱和一个直接后继:在树形结构中,数据元素之间有着明显的层次关系,并且每一 ...