线性时间选择

一、概念

给定线性序集中n个元素和一个整数k,1\leq k\leq n,要求找出这n个元素中第k小的元素。

二、特殊情况(堆排序)

k\leq n/\log nk\geq n-n\log n时,
使用堆排序可以在O(n+k\log n)=O(n)时间内找出第k小元素。

注:算法中log是以2为底的

三、一般情况

1.思路

模仿快速排序算法,对输入数组进行递归划分。与快速排序不同的是,它只对划分出的子数组之一进行递归处理。

2.缺点

在最坏情况下,随机划分需要\Omega(n^2)时间计算。

3.改进思路

如果能在线性时间内找到一个划分基准,使得按这个基准所划分出的两个子数组长度都至少为原数组长度的\varepsilon倍(0<\varepsilon<1是某个正常数),那么在最坏情况下用O(n)时间就可以完成选择任务。例如,若\varepsilon=9/10,算法递归调用所产生的子数组的长度至少缩短1/10。所以,在最坏情况下,算法所需的计算时间T(n)满足递归式T(n)\leq T(9n/10)+O(n)。由此可得T(n)=O(n)

4.改进方法

(1)将n个输入元素划分成\lceil n/5\rceil个组,每组5个元素,除可能有一个组不是5个元素外。用任意一种排序算法,将每组中的元素排好序,并取出每组的中位数,共\lceil n/5\rceil个。
(2)递归调用Select找出这\lceil n/5\rceil个元素的中位数。如果\lceil n/5\rceil是偶数,就找它的两个中位数中较大的一个。然后以这个元素作为划分基准。

5.C++代码

#include <iostream>

using namespace std;

template<class Type>
void Swap(Type &a, Type &b) {
    Type c = a;
    a = b;
    b = c;
}

//选择排序
template<class Type>
void SelectSort(Type a[], int p, int r) {
    for (int i = p; i < r; ++i) {
        int index = i;
        for (int j = i + 1; j <= r; ++j) {
            if (a[j] < a[index])
                index = j;
        }
        Swap(a[i], a[index]);
    }
}

//按x划分,返回划分基准下标
template<class Type>
int Partition(Type a[], int p, int r, Type x) {
    int i = p - 1, j = r + 1;
    while (true) {
        while (a[++i] < x && i < r);
        while (a[--j] > x && j > p);
        if (i >= j)break;
        Swap(a[i], a[j]);
    }
    return j;
}

//找到中位数(用于找每组的5个数的中位数)
template<class Type>
int SearchMid(Type a[], int p, int r) {
    Type *b = new Type[r - p + 1];
    for (int i = p; i <= r; ++i) {
        b[i - p] = a[i];
    }
    SelectSort(b, 0, r - p);
    for (int i = p; i <= r; ++i) {
        if (a[i] == b[(r - p + 1) / 2])
            return i;
    }
    return 0;
}

//p第一个数下标,r最后一个数下标,k要找的第k个数
template<class Type>
Type Select(Type a[], int p, int r, int k) {
    //规模小于75时直接排序查找
    if (r - p < 75) {
        SelectSort(a, p, r);
        return a[p + k - 1];
    }
    //分成n/5组,每组5个;找到每组中位数,放置队首
    for (int i = 0; i <= (r - p - 4) / 5; ++i) {
        int mid = SearchMid(a, p + 5 * i, p + 5 * i + 4);
        Swap(a[mid], a[p + i]);
    }
    //找到所有中位数的中位数
    Type x = Select(a, p, p + (r - p - 4) / 5, (r - p - 4) / 10 + 1);
    //按中位数划分
    int i = Partition(a, p, r, x), j = i - p + 1;
    if (k <= j)return Select(a, p, i, k);
    else return Select(a, i + 1, r, k - j);
}

int main() {
    return 0;
}

6.复杂度分析

为了分析算法Select的计算时间复杂性,设n=r-p+1,即n为输入数组的长度。

  • 算法的递归调用只有在n\geq75时才执行。因此,当n<75时,算法Select所用的计算时间不超过一个常数C1
  • 找到中位数的中位数x后,算法Select以x为划分基准调用函数Partition对数组a[p:r]进行划分,这需要O(n)时间。
  • 算法Select的for循环体共执行n/5次,每一次需要O(1)时间。因此,执行for循环共需O(n)时间。

设对n个元素的数组调用Select需要T(n)时间,那么找中位数的中位数x至多用T(n/5)时间。现已证明,按照算法所选的基准x进行划分所得到的两个子数组分别至多有3n/4个元素。所以无论对哪一个子数组调用Select都至多用T(3n/4)时间。
总之,可以得到关于T(n)的递归式T(n)\leq\begin{cases}C1&n<75\\C2n+T(n/5)+T(3n/4)&n\geq75\end{cases}
解此递归式可得T(n)=O(n)

7.其他

  • 由于算法将每一组的大小定位5,并选取75作为是否进行递归调用的分界点。这两点保证了T(n)的递归式中两个自变量之和n/5+3n/4=19n/20=\alpha n0<\alpha<1。这是使T(n)=O(n)的关键之处。当然,除了5和75之外,还有其他选择。
  • 在算法Select中,假设所有元素互不相等,这是为了保证在以x为划分基准调用函数Paritition对数组a[p:r]进行划分之后,所得到的两个子数组的长度都不超过原数组长度的3/4。当元素可能相等时,应在划分之后加一条语句,将所有与基准x相等的元素集中在一起,如果这样的元素的个数m\geq1,而且j\leq k\leq j+m-1时,就不必再递归调用,只要返回a[i]即可。否则最后一行改为调用Select(i+m+1, r, k-j-m)
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容

  • 1 初级排序算法 排序算法关注的主要是重新排列数组元素,其中每个元素都有一个主键。排序算法是将所有元素主键按某种方...
    深度沉迷学习阅读 1,389评论 0 1
  • 概述 排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部...
    蚁前阅读 5,164评论 0 52
  • 一些概念 数据结构就是研究数据的逻辑结构和物理结构以及它们之间相互关系,并对这种结构定义相应的运算,而且确保经过这...
    Winterfell_Z阅读 5,648评论 0 13
  • 版本记录 前言 app中好的炫的动画可以让用户耳目一新,为产品增色不少,关于动画的实现我们可以用基本动画、关键帧动...
    刀客传奇阅读 1,879评论 2 1
  • 1 站在悬崖上鸟瞰四周迷人风光,冷不丁被人一推直线下坠,本以为粉身碎骨,谁料想奇迹般地在谷底存活下来。 虽然遍体磷...
    梦想2050阅读 249评论 2 7