《算法之美》---次序选择问题

算法复习——分而治之篇之次序选择问题 算法复习——分而治之篇之次序选择问题 以下内容主要参考中国大学MOOC《算法设计与分析》,墙裂推荐希望入门算法的童鞋学习! 1. 问题背景 最小值查找: 例如给定数组A[1..16]A[1..16]A[1..16],寻找其中最小值,如下图所示。 依次扫描,记录最小值 那么,对问题稍加改变,如何求得数组中第kkk小的元素? 2. 问题定义 次序选择问题(Selection Problem) 输入: 包含nnn个不同元素的数组A[1..n]A[1..n]A[1..n] 整数k(1≤k≤n) 阅读详情

当一个序列排好序之后,我们一般会对其有两种操作:一是对其进行查找操作;一是得知任何特定元素在一个序列中的次序。当如果我们仅仅想知道某一个或某几个元素在序列里的次序,或者处于某个特定位置上的元素,则并不一定需要对整个序列进行排序。

在一个序列里面挑选处于特定位置的元素的问题就是所谓次序选择问题。一般定义如下:在n个元素里面选择出第i小(或大)的元素,即找出排名为i的元素。

快速次序选择算法是比直接对序列进行排序更优的次序选择算法,这个算法从快速排序算法改进而来。快速次序选择算法伪代码如下:

ORDER-SELECTION(A, p, q, i)     //在序列A[p…q]中选择第i小的元素

         if p=q then return A[p]         //已经找到第i小的元素

         r <-- PARTITION(A, p, q) //调用快速排序的分解算法

         k <-- r-p+1               //k = rank(A[r])

         if i=k then return A[r]  //已经找到第i小的元素

         if i<k then return ORDER-SELECTION(A, p, r-1, i) //在序列A[p…r-1]中选择第i小的元素

         else return ORDER-SELECTION(A, r+1, q, i-k)   //在序列A[r+1…q]中选择低i-k小的元素

 

如上伪代码所示,如果A[r]的次序(即代码里的变量k)不是序列里第i小的元素,则我们有两种情况:

1)A[r]的次序小于i,因此第i小的元素在元素A[r]的左边,即A[p…r-1]里。此时我们需要在A[p…r-1]里寻找第i小的元素;

2)A[r]的次序大于i,因此第i小的元素在元素A[r]的右边,即A[r+1…q]里。此时,我们需要在A[r+1…q]里寻找第i小的元素。不过此时我们已经丢掉了A[p…r]里面的所有元素,因此,整个序列里面第i小的元素在A[r+1…q]里则是第i-k小的元素。

 

快速次序选择最后情况下和平均情况下时间复杂度是Θ(n),最坏情况下是Θ(n2)。

 

快速选择算法虽然在多数情况下的时间成本是线性的,但最坏情况下却是平方级的。为了防止提供数据的对手使得排序情况总是最坏的,我们使用随机化手段,即得到如下的随机快速次序选择算法:

RANDOM-SELECTION(A, p, q, i)        //在序列A[p…q]中选择第i小的元素

         if p=q then return A[p]         //已经找到第i小的元素

         r <--RAND-PARTITION(A, p, q) //调用随机快速排序的分解算法

         k <-- r-p+1               //k = rank(A[r])

         if i=k then return A[r]  //已经找到第i小的元素

         if i<k then return RANDOM-SELECTION(A, p, r-1, i) //在序列A[p…r-1]中选择第i小的元素

         else return RANDOM-SELECTION(A, r+1, q, i-k)      //在序列A[r+1…q]中选择低i-k小的元素

此算法和快速选择算法唯一的不同是在分解的时候,不是选择A[p]作为杠杆点,而是从序列A[p…q]中随机选择一个元素作为杠杆点。

 

随机快速次序选择算法的实现代码如下:(摘自BlogJava)

#include <stdio.h>

#include <stdlib.h>

int new_random(int min, int max)

{

    return (min + (int)(((float)rand()/RAND_MAX)*(max - min)));

}

void swap(int *a, int *b)

{

    int c = *a;

    *a = *b;

    *b = c;

}

 

int partition(int A[], int p, int r)

{

    int i = p - 1, j;

    for(j = p; j < r; j++)

    {

        if(A[j] <= A[r])

        {

            i++;

            swap(&A[i], &A[j]);

        }

    }

    swap(&A[i + 1], &A[r]);

    return i + 1;

}

 

int randomize_partition(int A[], int p, int r)

{

    int i = new_random(p, r);

    swap(&A[i], &A[r]);

    return partition(A, p, r);

}

 

int randomized_select(int data[], int p, int r, int k)

{

    if(k > (r - p + 1)) return 0;

    if(p == r) return data[p];

    int i = randomize_partition(data, p, r);

 

    int count = i - p + 1;

    if(k <= count)

        return randomized_select(data, p, i, k);

    else

        return randomized_select(data, i + 1, r, k - count);

}

 

虽然随机快速次序选择问题在实际使用中效率很高,只要不是每次都运气很差的话,就可以保证线性时间的时间效率,而运气不差的关键是杠杆点选择的不是数组里面最小或最大的元素。即如果能够保证不选择序列里最大或最小的元素作为杠杆点,我们就能保证随机快速次序选择算法时间成本是线性的。

下面要讨论的算法就是用来保证杠杆点不选到序列中的最大值或最小值。这就是最坏情况下的线性选择算法,它是由Blum、Floyd、Pratt、Rivest和Tarjan在1973年提出的,该算法的核心就是尽量选择一个接近中间的杠杆点,伪代码如下:

LINEAR-SELECTION(i, n)      //在n个元素里面找出第i小的元素

         将n个元素分解为5元组,直接用蛮力找出每个5元组的中位数

         在获得的所有中位数中,递归找出它们的中位数x

         将元素按照最后获得的中位数进行分解,设k=rank(x)

         if i==k then

                   return x   //已经找到所需元素,结束算法

         else if i<k then

                   在小于x的部分递归选择第i小的元素

         else

                   //在大于x的部分递归选择第i-k小的元素

 

最坏情况下线性选择算法的代码实现如下:(摘自BlogJava,接上面代码)

void quicksort(int data[], int b, int e)

{

    if(b < e)

    {

        int k = partition(data, b, e);

        quicksort(data, b, k - 1);

        quicksort(data, k + 1, e);

    }

}

 

int partition1(int A[], int p, int r, int x)

{

    int i = p - 1, j;

    for(j = p; j <= r; j++)

    {

        if(A[j] <= x)

        {

            i++;

            swap(&A[i], &A[j]);

        }

    }

    A[i + 1] = x;

    return i + 1;

}

 

int select_new(int data[], int p, int r, int k)

{

    if(r - p < 75)

    {

        quicksort(data, p, r);

        return data[p + k - 1];

    }  

    int i;

    for(i = 0; i <= (r - p - 4) / 5; i++)

    {

        quicksort(data, p + 5 * i, p + 5 * i + 4);

        swap(&data[p + 5 * i + 2], &data[p + i]);

    }

    int x = select_new(data, p, p + (r - p - 4) / 5, (r - p - 4)/10); // 得到更好的轴X

    i = partition1(data, p, r, x);

    int count = i - p + 1;

    if(k <= count)

        return select_new(data, p, i, k);

    else

        return select_new(data, i + 1, r, k - count);

}

 

测试代码如下:(接上面代码)

int main()

{

    int data[] = {3, 1, 7, 34, 8, 11, 678, 12, -1, 100};

    printf("%d/n", randomized_select(data, 0, 9, 2));

    int data1[] = {3, 1, 7, 34, 8, 11, 678, 12, -1, 100};

    printf("%d/n", select_new(data1, 0, 9, 2));

  

     return 0;

}

 

虽然最坏情况下线性选择算法在最坏情况下的时间复杂度是线性级的,但它在实际中的运行效率却不如随机快速次序选择算法,这是因为该算法效率虽然是cn,但常数系数c的值很大!而且该算法的思路比随机快速次序选择算法负责,较难理解,因此,该算法除了在无聊时锻炼下脑筋,打发下时间之外,几乎没什么其他用处!

=======================================================

附:

常见的次序选择问题总结如下:

其中|m|表示对m向上取整;[m]表示对m向下取整。

类型1:选序列中的最大值:

算法思想或实现:

template<typename T>

T FindMax(T data[], int size)

{

         T max = data[0];

         for(int i=1; i<size; ++i)

         {

                   if(max < data[i])

                            max = data[i];

         }

         return max;

}

 

该算法最坏情况下的时间复杂度是O(n);在n个数的数组中找最大的数,并以比较作为基本的运算的算法类中,任何算法在最坏情况下至少要做n-1次比较。这是因为最大值max是惟一的,其他n-1个数必须在比较后才可能被淘汰,一次比较最多淘汰一个数,因此至少需要比较n-1次。FindMax算法是最优的了。

 

类型2:找序列中的最大值和最小值:

算法思想或实现:

1)将n个元素两两一组分为[n/2]组;

2)每组内两个数做比较,得到[n/2]个较小值和[n/2]个较大值;

3)在[n/2]个(若n为奇数时是[n/2]+1)较小值中找出最小值min;

4)在[n/2]个(若n为奇数时是[n/2]+1)较大值中找最大值max;

 

类型3:找序列中第二大值:

算法思想或实现:

锦标赛方法,最优!

1)将元素个数n赋值给变量k,因为k在后面循环中值要改变,所以不能直接用n值;

2)将k个元素分为两两一组,即分成了k/2组;

3)每组中2个数比较,找出较大值;

4)将被淘汰的较小的值在淘汰它的较大的值所维护的链表中做记录;

5)如果k是奇数,则k=k/2+1;否则k=k/2;

6)如果k>1,则转到2);

7)max = 最大值;

8)secondMax = max维护的链表中的所有数的最大值;

 

类型4:一般性选择问题:

输入:数组data[n],正整数k,1<=k<=n

输出:第k小的数

 

算法思想或实现:

Select(data, k):

1)将data划分成5个一组,共m=[n/5]组;

2)每组找中位数,找到的m个中位数放到数组M中;

3)m*=Select(M, |M|/2),接着将data中的数划分成A、B、C和D四个集合;

4)把A和D中的每个元素与m*比较,小的构成S1,大的构成S2;

5)S1=S1并C;S2=S2并B;

6)如果k==|S1|+1,则输出m*;

7)否则,如果k<=|S1|,则Select(S1, k);

8)否则Select(S2, k-|S1|-1)。

 

类型5:给定n个不同的数的集合S和正整数i,求最大的i个数;

算法A:调用i次找最大值算法FindMax,每调用一次从S中删除一个最大的数;

算法B:对S排序,并输出S中最大的i个数;

问:1)分析A、B两个算法在最坏情况下的时间复杂度;

2)试设计一个最坏情况下时间复杂度的阶更低的算法。

 

算法A的最坏时间复杂度:i*n = O(n3/2);

算法B的最坏时间复杂度:O(nlogn)。

 

算法思路或实现:

设k表示第i大的元素在排好序数组中的下标,元素记为x;

用选择算法确定这个元素,用x划分数组S,将比x大的放到x的后面;

对S中从k到n的元素进行排序,之后倒序输出即可。

 

输入:集合S

输出:S中最大的i个数

1)k=n-i+1;

2)x=Select(S, n, k);

3)用x划分S,将S中比x大的元素放到数组的k+1到n个位置;

4)对S[k…n]排序;

5)倒序输出

 

复杂度:O(n)+O(i*logi)。

 

 

 

 

 

C++实现 多处最优服务次序问题 题目:设有n个顾客同时等待一项服务。顾客i需要的服务时间为ti,1<=ti<=n。共有s处可以提供此项服务。应如何安排n个顾客的服务次序才能使平均等待时间达到最小? 平均等待时间是n个顾客等待服务时间的总和除以n。 输入示例: n = 10, s = 2 56 12 1 99 1000 234 33 55 99 812 output: 336 注意:在这个问题里面,将服务时间与等待时... 阅读详情

相关推荐

algorithms(算法之美).pdf.zip

like Gutenberg probably only knew how to add and subtract small numbers using his fingers; for anything more complicated he had to consult

【算法(三·五):分治思想——次序选择问题】

本文介绍了次序选择问题的思想、实现过程及其与快速排序思想、所遇问题的比较。

计算机领域博客,与时俱进的思想与技术。 876

OpenGL绘制简单场景,实现旋转缩放平移和灯光效果

本项目实现了用OpenGL绘制一个简单场景,包含正方体、球体和网格,实现了物体的旋转、缩放、平移和灯光效果。附有项目完整代码,有详细注释。适合初学者熟悉opengl使用。

张竞成的博客 1万+

次序选择问题

给定一个数组A,求出第k小的元素。 这是一个次序选择问题,求解方法多种,此处介绍在快排的思想上进行改造的分治算法。 原数组 原数组通过一次分解,求出随机选出的新主元的位置,该位置返回到主递归函数赋值给变量q 比较k和q的位置,若k在q的右边,则只对右边进行进一次拆分,若为左边则相反。直到q的位置刚好对应的是k所表达的位置,则此时q位置的元素为第k小的元素,因为它左边的都比它小,右边的都比它大。比较这一步要注意。 #include<iostream> using namespace std;

drdtddtrdr的博客 1762

Surface Go 2 变身 Linux 开发本:保姆级 Ubuntu 20.04 双系统安装与驱动配置全攻略

本文详细介绍了如何将 Surface Go 2 变身为高效的 Linux 开发本,涵盖 Ubuntu 20.04 双系统安装、专用内核配置、驱动优化等全流程。通过 linux-surface 内核安装和硬件驱动优化,确保 Surface Go 2 的触控、笔输入、摄像头等功能完美运行,适合开发者打造便携生产力工具。

weixin_30596165的博客 616

绕过Google Play:获取Expo Go安卓APK的实用指南

本文为国内安卓开发者提供了绕过Google Play商店获取Expo Go APK的实用指南。针对无法访问Google Play的痛点,详细介绍了通过官方GitHub Releases、可信第三方网站(如APKMirror)及APK下载器等多种安全获取途径,并重点讲解了APK安装、安全验证(核对签名)及常见连接问题的排查方法,帮助开发者顺利开启React Native开发之旅。

weixin_29006701的博客 577

次序选择问题——分治法

问题背景:最小值查找,查找指定位置的元素 k为指定位置,q为主元位置,p为数组起始位置,q为数组结束位置, 该问题可参考快速排序

qq_46591394的博客 819

信创改造实战:SpringBoot项目迁移到宝兰德BES 9.5.5的完整避坑指南(附依赖配置)

本文详细介绍了将SpringBoot项目迁移到宝兰德BES 9.5.5中间件的完整流程,包括环境准备、依赖调整、Web.xml版本适配、部署配置优化及常见问题解决方案。特别针对信创改造需求,提供了排除Tomcat依赖、处理BES专用starter等实用技巧,帮助开发者顺利完成国产中间件迁移。

weixin_30825581的博客 1098

算法设计一:分而治之

分而治之解决问题思路: 分解原问题 :原问题分解成多个子问题 解决子问题 :递归地求解各个子问题 合并问题解 :将结果合并为原问题解 1. 归并排序 思路 归并排序:分解数组,递归求解,合并排序 **分解原问题:**将数组A[1,n]分解成A[1,n/2]和A[n/2+1,n]子数组排序问题 **解决子问题:**递归解决子问题得到有序的子数组 合并问题解:最后合并成有序的数组 实现 伪代码+Java 伪代码: Java代码实现: 时间复杂度为????(???? ??????????

Zeroowt的博客 544

《数据结构》04-树4 是否同一棵二叉搜索树

题目 给定一个插入序列就可以唯一确定一棵二叉搜索树。然而,一棵给定的二叉搜索树却可以由多种不同的插入序列得到。例如分别按照序列{2, 1, 3}和{2, 3, 1}插入初始为空的二叉搜索树,都得到一样的结果。于是对于输入的各种插入序列,你需要判断它们是否能生成一样的二叉搜索树。 输入格式: 输入包含若干组测试数据。每组数据的第1行给出两个正整数N (≤10)和L,分别是每个序列插入元素的个数和需要...

叫我皮卡丘 2012

美特CRM mcc_login.jsp存在SQL注入漏洞

MetaCRM 是一种现代化的客户关系管理(CRM)平台,旨在通过集成和自动化的方式帮助企业管理与客户的关系,提高销售和服务效率。MetaCRM 的功能和特性通常涵盖了多个领域,包括销售、营销、客户服务、数据分析和自动化工具。

缘梦未来的博客 808

【贪心算法】最优服务次序问题

算法实现题 4-6最优服务次序问题 设有n个顾客同时等待一项服务。顾客i需要的服务时间为ti。应如何安排n个顾客的服务次序才能使平均等待时间达到最小?平均等待时间是n个顾客等待直到完成服务的时间总和除以n。 对于给定的n个顾客需要的服务时间,编程计算最优服务次序。 Input 测试数据第一行是正整数n(n<=1000),表示有n个顾客。接下来的1行中,有n个正整数,表示n个顾客需要的服务时间ti(ti<=1000)。 Output 输出最小平均等待时间,每个答案一行,保留2位小数。.

WR的博客 1万+

virtuoso如何导出def?

在DEF Out界面,填写def name,library name以及cell name ,cell view既layout,把Output "Floating Shapes" into SPECIAL NETS Section勾上,OK即可。2)在要导出的layout界面选中PAD和prBoundary(shift+左键),快捷键C copy到空cell里,这时需要调整copy过来的object坐标,让两边prBoundary的坐标相同。我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧?

m0_61544122的博客 2306

次序选择问题 java代码完整实现 分治法

什么是次序选择: 就是需要找出给定序列中第k小元素(其中k大1小于n)。 比如当k=3时,我们需要找出该序列中第三小元素。 分治法解决次序选择问题: 我们可以借用随机主元的快速排序的思维去解决该问题(点击查看本人另一篇博文随机化快速排序:随机化快速排序),与快速排序不同的是,在主元下标等于k时,我们就可以直接退出,不需要再进行剩下的分解问题和合并问题解过程。其时间复杂度的数学期望为O(n),是一个非常高效的算法。 比如我们要寻找第4小元素,给定数组为945721683,下面我们对其进行分析: 开始——&gt

MrYushiwen的博客 2148

Selenium|通过更改亚马逊amazon的配送地获取当地的cookie

如果我们需要拿到国外配送地的商品信息,这个时候就需要用到selenium或者pypputeer自动化的方法去更改配送地,拿到当地的cookie信息,之后,携带当地的cookie使用requests发送请求,就可以拿到国外配送地的商品数据信息。一般来说,我们访问亚马逊,亚马逊的配送地一般显示的是国内,但是国内的商品信息和国外的商品信息其实是存在很大差别的,比如你在搜索框输入某个关键词,看到的

Python进阶专栏《爬虫实战进阶》,《Pyppeteer从入门到精通》原创作者 993

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

分治思想的快速排序——次序选择问题

讲解次序查找问题,穿插快速排序和合并排序利用分治思想

MYNFR的博客 824

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

最优服务次序问题

最优服务次序问题 一、问题描述: 设有n 个顾客同时等待一项服务。顾客i需要的服务时间为ti, 1≦i ≦n 。共有s处可以提供此服务。应如何安排n个顾客的服务次序才能使平均等待时间达到最小?平均等待时间是n 个顾客等待服务时间的总和除以n。 二、贪心选择策略 假设原问题为T,而我们已经知道了某个最优服务系列,即最优解为A={t(1),t(2),….t(n)}(其中t(i)为第i个用户需

I Have A Dream 2391

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

最优服务次序C语言,最优服务次序问题 和 汽车加油问题

1.最优服务次序问题问题描述:设有n个顾客同时等待一项服务。顾客i需要的服务时间为ti,1≦i≦n。共有s处可以提供此服务。应如何安排n个顾客的服务次序才能使平均等待时间达到最小平均等待时间是n个顾客等待服务时间的总和除以n。算法设计:对于给定的n个顾客需要的服务时间,计算最优服务次序。数据输入:由文件input.txt给出输入数据。第一行是正整数n,表示有n个顾客。接下来的1行中,有n...

weixin_29817791的博客 1336

学习贪心算法之最优服务次序问题

最优服务次序问题 一、问题描述:     设有n个顾客同时等待一项服务。顾客i需要的服务时间为ti, 1≦i ≦n 。共有s处可以提供此服务。应如何安排n个顾客的服务次序才能使平均等待时间达到最小?平均等待时间是n 个顾客等待服务时间的总和除以n。 二、贪心选择策略  假设原问题为T(先假设只有一个服务点),而我们已经知道了某个最优服务系列,即最优解为  A={t(1),t(2)

qq_33859629的博客 2429

多处最优服务次序问题(C语言)--贪心策略

多处最优服务次序问题 题目描述 设有n个顾客同时等待一项服务,顾客i需要的服务时间为ti,1≤i≤n,共有s处可以提供此项服务。应如何安排n个顾客的服务次序才能使平均等待时间达到最小? 平均等待时间是n个顾客等待服务时间的总和除以n。 解题思路 假设原问题为T(先假设只有一个服务点),而我们已经知道了某个最优服务系列,即最优解为A={t(1),t(2),….t(n)}(其中t(i)为第i个用户需要...

一个巨大的博客 1万+

《算法王晓东》多处最优服务次序问题

设有n个顾客同时等待一项服务。顾客i需要的服务时间为ti, 1≤i≤n。共有s处可以提供此项服务。应如何安排n个顾客的服务次序才能使平均等待时间达到最小?平均等待时间是n个顾客等待服务时间的总和除以n。算法设计:对于给定的n个顾客需要的服务时间和s的值,计算最优服务次序。

m0_51376859的博客 1057
上一篇: 《算法之美》---快速排序(随机快速排序)
下一篇: 《ASCE1885的IT笔试面试题》---0000 0004
ACE1985
博客等级 码龄17年 1307粉丝 308原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值