转载自:https://www.cnblogs.com/Not-Famous/p/3653945.html

也可参考:https://www.jianshu.com/p/bf47d7b4a43d 或 https://www.bbsmax.com/A/D854V0EQzE/

线性时间选择(Linear Select): 这个名字不太好理解,什么叫线性时间选择?一句话,在线性时间内完成选择。一般情况下是这样的,我们想要找出一个数组中的最大值或最小值,那就只需要一次排列,然后输出第一个或最后一个元素就行了,但如果是要找出一个数组中的第 k 小的元素呢?

在一般情况下,可以用 RandomizedSelect 方法来找出第 k 小的元素,平均时间是 O (n),但在最坏情况下,所用的时间则是 n^2,因此,本文讨论的就是在最坏情况下,如何在 O (n) 时间内完成选择。算法的思路总体有些复杂,但每一步其实不难,下面即给大家介绍最坏情况下的线性时间选择算法。

(1):将 n 个输入元素以每组 5 个地划分,共划分出 (n/5) 个组,每个组分别进行排列,找出中位数,然后按照每个组的顺序,把每个组的中位数与整个数组的前 (n/5) 个数交换;

(2):那么,前 (n/5) 个数就是各组的中位数了,然后,我们通过 select 方法找出这些中位数的中位数,以这个中位数的中位数为基准,调用 partition 方法;

(3):调用了 partition 方法后的基准元素正是处于数组的正确位置(前边的元素都比基准元素小,后边的元素都比基准元素大),记下基准元素前边的元素个数 leftNum,如果 k 小于或等于 leftNum,则在基准位置前的这部分调用 select 方法即可,如果在 k 大于 leftNum,则在基准位置后的这部分调用 select 方法。

下面,我直接把代码贴出,读者可以通过我的注释来理解每一步的意义。
复制代码

 1 private static int select(int[] a,int l,int r,int k){2     if(r - l < 75){3         insertSort(a, l, r);    //用快速排序进行排序4         return a[l + k - 1];5     }6     int group = (r-l+5)/5;7     for(int i = 0;i<group;i++){8         int left = l+5*i;9         int right = (l + i * 5 + 4) > r ? r : l + i * 5 + 4;  //如果超出右边界就用右边界赋值
10         int mid = (left+right)/2;
11         insertSort(a, left, right);
12         swap(a, l + i, mid);     // 将各组中位数与前i个
13     }
14     int pivot = select(a,l,l+group-1,(group+1)/2);  //找出中位数的中位数
15     int p = partition(a,l,r,pivot);    //用中位数的中位数作为基准的位置
16     int leftNum = p - l;       //leftNum用来记录基准位置的前边的元素个数
17     if (k == leftNum + 1)
18         return a[p];
19     else if (k <= leftNum)
20         return select(a, l, p - 1, k);
21     else                    //若k在基准位子的后边,则要从基准位置的后边数起,即第(k - leftNum - 1)个
22         return select(a, p + 1, r, k - leftNum - 1);
23 }

复制代码

到此大家也可以看出,这里的 partition 方法与前边讲到过的快速排序所用到的 partition 方法稍有不同,参数个数都变了,但其实变化只是很小,只是取消了一开始定义基准位置的步骤而已,代码如下:
复制代码

 1 private static int partition(int[] a,int l,int r,int pivot){   //适用于线性时间选择的partition方法2     int i = l;3     int j = r;4     while(true){5         while(a[i] <= pivot && i < r)6             ++i;   //i一直向后移动,直到出现a[i]>pivot7         while(a[j] > pivot)8             --j;   //j一直向前移动,直到出现a[j]<pivot9         if(i >= j) break;
10         swap(a,i,j);
11     }
12     a[l] = a[j];
13     a[j] = pivot;
14     return j;
15 }

复制代码

下面是 select 方法中,如果输入规模小于 75 时用到的插入排序算法代码:
复制代码

 1 private static void insertSort(int[] a, int law, int high) {    //插入排序2        for (int i = law + 1; i <= high; i++) {  3            int key = a[i];  4            int j = i - 1;  5            while (j >= law && a[j] > key) {  6                a[j + 1] = a[j];  7                j--;  8            }  9            a[j + 1] = key;
10        }
11 }

复制代码

适用于数组元素之间的 swap 方法如下:

1 private static void swap(int[] a,int i,int j){2     int temp = a[i];
3     a[i] = a[j];
4     a[j] = temp;
5 }

各位可能有个疑问,为什么输入规模不足 75 时调用插入排序而不用线性时间选择呢?那是因为当输入规模不足 75 时,因为输入规模太小,时间复杂度几乎是一个常量,因此没有必要用到比较复杂的线性时间选择算法。

我还看到一个比较好懂的学习线性时间选择的动画,能形象地看到线性时间选择的执行过程,链接如下:

http://resource.jingpinke.com/details?uuid=ff808081-22e8911b-0122-e8912643-048d&objectId=oid:ff808081-22e8911b-0122-e8912643-048e

如果有不足之处或者对该算法有更好的建议,请提出!

算法之线性时间选择(最坏情况下)相关推荐

  1. Leapfrog Triejoin:最坏情况下的最优连接算法

    介绍 leapfrog triejoin是商业数据记录系统 LogicBlox® 采用的一种新颖的连接算法,在不同的基准测试中表现出色.leapfrog triejoin论文的写作者认为这个算法,即使 ...

  2. 两个鸡蛋测试:从100层楼往下扔鸡蛋,求最坏情况下确认保证鸡蛋可以不破的最大楼层所需次数

    最坏情况下求得最优解所需的次数 内容说明 本文是在看过<<妙解谷歌压箱底面试题:如何正确的从楼上抛鸡蛋>>一文以后做的总结,该文章对此问题描写的很详细,但是在拜读的过程中也花了 ...

  3. 连接定义点作用_最坏情况下最优连接(Worst-Case Optimal Joins)

    所谓最坏情况下最优连接(Worst-Case Optimal Joins),是一项关于数据库中连接操作的最新技术.给定若干表{R1, R2, ..., Rn},在它们之上的多表连接所能得到结果的数量上 ...

  4. 下列各排序法中,最坏情况下的时间复杂度最低的是(**C** )A.希尔排序 B.快速排序 C.堆排序 D.冒泡排序

    下列各排序法中,最坏情况下的时间复杂度最低的是(C ) 希尔排序 A.快速排序 B.堆排序 C.冒泡排序 D.正确答案:C 题目解析: 堆排序最坏情况时间下的时间复杂度为 O(nlog2n) :希尔排 ...

  5. 有一栋楼共100层,一个鸡蛋从第N层及以上的楼层落下来会摔破, 在第N层以下的楼层落下不会摔破。给你2个鸡蛋,设计方案找出N,并且保证在最坏情况下, 最小化鸡蛋下落的次数。

    有一栋楼共100层,一个鸡蛋从第N层及以上的楼层落下来会摔破, 在第N层以下的楼层落下不会摔破.给你2个鸡蛋,设计方案找出N,并且保证在最坏情况下, 最小化鸡蛋下落的次数.(假设每次摔落时,如果没有摔 ...

  6. 设有6个有序表A、B、C、D、E、F,分别含有10、35、40、50、60和200个数据元素,各表中元素按升序排列。要求通过5次两两合并,将6个表最终合并成1个升序表,并在最坏情况下比较的总次数达到最

    设有6个有序表A.B.C.D.E.F,分别含有10.35.40.50.60和200个数据元素,各表中元素按升序排列.要求通过5次两两合并,将6个表最终合并成1个升序表,并在最坏情况下比较的总次数达到最 ...

  7. 数据结构与算法 / 冒泡排序最坏情况下的时间复杂度解析

    冒泡排序是一种用时间换空间的排序方法,最坏情况是把顺序的排列变成逆序,或者把逆序的数列变成顺序.在这种情况下,每一次比较都需要进行交换运算. 举个例子来说,一个数列 5 4 3 2 1 进行冒泡升序排 ...

  8. 算法练习——在有序序列(r1,r2,...,rn)中,存在序号i(1<=i<=n),使得ri=i。请设计一个分治算法找到这个元素。 要求算法在最坏情况下的时间性能为O(logn))

    算法练习 题目 答案 注意 题目 答案 #include<iostream> using namespace std; int find(int a[],int left,int righ ...

  9. 数据结构 | 合并两个长度分别为m和n的有序表,最坏情况下需要比较m+n-1次

    最坏的情况就是交叉 如: 1 3 5 2 4 6 与 1 3 5 7 9 2 4 8 设上链指针p,下链q,每次比较后较小节点依次作为"合并后链表的节点",同时较小链指针后移.某链 ...

  10. 最坏情况为线性时间的选择算法(SELECT)

    该文章参考(代码参考进行修改已经验证)--来源作者博客 算法思想 1.将数组的 n 个元素划分为 [n/5](向下取整)组,每一组5个元素,且至多只有一组由剩下的 n mod 5 个元素组成 2.寻找 ...

最新文章

  1. 盘点我跳过的科研天坑,进坑就是半年白干
  2. Java中List高效去重
  3. 用django2.1开发公司官网(上)
  4. Java JNI浅析(一)
  5. Object-c基础(2)
  6. 写JQuery 插件 什么?你还不会写JQuery 插件
  7. WinCE6下的kernelIoControl使用方法
  8. oracle 批量 重建索引,Oracle重建索引Shell脚本、SQL脚本分享
  9. 使用Python解析MNIST数据集
  10. pandas的自带数据集_pandas.DataFrame.sample随机抽样
  11. 2019 蓝桥杯省赛 B 组模拟赛(一) J. 程序设计:蒜厂年会 环形连续子序列求和问题
  12. 欢迎界面java_Linux命令行欢迎界面美化
  13. shell第四次练习
  14. 【UVA11168】Airport(凸包+点到直线距离(一般式))
  15. nide-js.nt
  16. Typescript 类型推断
  17. jeeplus2.0 ani 版 定时任务的创建
  18. 新能源充电桩:特来电、小鹏汽车加速跑
  19. PHP有三宝,三、认识三宝
  20. AD使用教程 图文并茂 AD2020四层板

热门文章

  1. G.8032协议 ERPS
  2. Matlab画图常用命令
  3. 算法之数学--常用数学公式,规律神器OEIS 2021-03-09
  4. cesium-加载DEM数据(可拉伸)
  5. 零基础使用 MATLAB 求解偏微分方程(建议收藏)
  6. 聊聊人像抠图背后的算法技术
  7. PDF文件打开密码解密
  8. windows下载mysql太慢
  9. 北理在线作业答案c语言,北理乐学C语言答案,最新.doc
  10. 几款开源的Windows界面库