简要介绍下快速排序的思想:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。时间复杂度为O(nlogn)

一.《data structure and algorithm analysis in c》中的实现,测试过,觉得该说明的已经注释

 C++ Code 
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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
 
#include<stdio.h>
#define LEN 15
#define CUTOFF 3
//用c++则可以写成引用
void swap(int *const p1, int *const p2)
{
    int tmp = *p1;
    *p1 = *p2;
    *p2 = tmp;
}
//插入排序
void insertion_sort(int a[], int n)
{
    int i, j;
    int tmp;
    for (i = 1; i < n; i++)
    {
        tmp = a[i];
        for (j = i; j > 0 && a[j - 1] > tmp; j--)
            a[j] = a[j - 1];
        a[j] = tmp;
    }
}

// return median of left, center, and right
// order these and hide the pivot
int median3(int a[], int left, int right)
{
    int center = (left + right) / 2;
    if (a[left] > a[center])
        swap(&a[left], &a[center]);
    if (a[left] > a[right])
        swap(&a[left], &a[right]);
    if (a[center] > a[right])
        swap(&a[center], &a[right]);
    //  invariant: a[left] <= a[center] <= a[right]
    swap(&a[center], &a[right - 1]);   //将中位数作为pivot且放置在right-1处
    return a[right - 1];                        //返回pivot
}

void qsort(int a[], int left, int right)
{
    int i, j, pivot;
    if (left + CUTOFF <= right)
    {
        //因为要递归调用,如果不截断判断则会导致数组访问越界进而出现段错误
        // left, left+1, ..., right-2, right-1, right 最极限的情况就是保证
        // left与right中间至少有一个值,即CUTOFF最小要等于2,否则出现段错误
        // CUTOFF=2保证可以取中位数
        // 当CUTOFF=1时不出现段错误,但运行结果是错误的
        pivot = median3(a, left, right);
        i = left;
        j = right - 1;
        for (; ;)
        {
            while (a[++i] < pivot) {} //median函数已经比较了left和right,pivot当前位置为right-1
            while (a[--j] > pivot) {} //故从left+1和right-2开始比较
            if (i < j)
                swap(&a[i], &a[j]);
            else
                break;
        }
        swap(&a[i], &a[right - 1]); //now i is the pivot index in the array
        qsort(a, left, i - 1);
        qsort(a, i + 1, right);
    }
    else   // do an insertion sort on the subarray
        insertion_sort(a + left, right - left + 1);
}

int main(void)
{
    int i;
    int arr[LEN] = {43, 423, 13, 6, 34, 64, 24, 69,
                    32, 28, 432, 641, 4365, 345, 624
                   };
    qsort(arr, 0, LEN - 1);
    for (i = 0; i < LEN; i++)
        printf("%d ", arr[i]);
    printf("\n");
    return 0;
}

二.不对pivot进行中位数取值的简易版本

 C++ Code 
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
34
35
36
37
38
39
40
41
42
43
44
45
46
 
#include<stdio.h>
#define LEN 15

void swap(int *const p1, int *const p2)
{
    int tmp = *p1;
    *p1 = *p2;
    *p2 = tmp;
}

void qsort(int a[], int left, int right)
{
    int i, j, pivot;
    pivot = a[right];   //the last item as pivot
    i = left;
    j = right - 1;
    if (left < right)
    {
        for (; ;)
        {
            for (; a[i] < pivot; i++);
            for (; a[j] > pivot; j--);
            if (i < j)
                swap(&a[i], &a[j]);
            else
                break;
        }
        swap(&a[i], &a[right]);   //now i is the pivot index in the array
        qsort(a, left, i - 1);
        qsort(a, i + 1, right);
    }
}

int main(void)
{
    int i;
    int arr[LEN] = {43, 423, 13, 6, 34, 64, 24, 69,
                    32, 28, 432, 641, 4365, 345, 624
                   };
    qsort(arr, 0, LEN - 1);
    for (i = 0; i < LEN; i++)
        printf("%d ", arr[i]);
    printf("\n");
    return 0;
}

三.根据简易快速排序得出的第k小选择算法

 C++ Code 
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
34
35
36
37
38
39
40
41
42
43
44
45
 
#include<stdio.h>
#define LEN 15
#define K 6

void swap(int *const p1, int *const p2)
{
    int tmp = *p1;
    *p1 = *p2;
    *p2 = tmp;
}

int  qsort(int k, int a[], int left, int right)
{
    int i, j, pivot;
    pivot = a[right];
    i = left;
    j = right - 1;
    for (; ;)
    {
        for (; a[i] < pivot; i++);
        for (; a[j] > pivot; j--);
        if (i < j)
            swap(&a[i], &a[j]);
        else
            break;
    }
    swap(&a[i], &a[right]);  //now i is the pivot index in the array
    //i.e. a[i] is the (i+1)th smallest item
    if (k == i - left + 1)
        return a[i];
    else if (k < i - left + 1)
        return qsort(k, a, left, i - 1); //target before pivot
    else                                           //target after  pivot
        return qsort((k - (i - left + 1)), a, i + 1, right);
}

int main(void)
{
    int arr[LEN] = {43, 423, 13, 6, 34, 64, 24, 69,
                    32, 28, 432, 641, 4365, 345, 624
                   };
    printf("%d\n", qsort(K, arr, 0, LEN - 1));
    return 0;
}

四.中位数之第k小的线性选择算法
实现该算法的步骤如下:
    1.如果n是一个比较小的数,比如n<6,那么只需要对此无序数组进行排序后,即可很容易的得到第K小元素。
此时约束时间T=7。
    2.如果n>5,那么我们将这个无序数组分成五组。此时约束时间T=n/5。
    3.找出每组的中位数,构成集合M。此时的约束时间T=7n/5.
    4.递归的调用selection(M,|M|/2)算法查找上一步中所有中位数的中位数,设为m。此时的约束时间
T=T(n/5)。
    5.用m来分割此时的数组,比较m与其他的(n-1)个数,小于m的数置于左集合L,大于m的数置于右集合R。当
然,中位数m的下标r=|L|+1(|L|是左集合L的个数)。此时的约束时间T=T(n)。

如果r=k,那么返回m。
    如果r<k,那么在小于m的左集合L中递归查找第K小数。

如果r>k,那么在大于m的右集合R中递归查找第K小数。

动态图示参见:http://ds.fzu.edu.cn/fine/resources/FlashContent.asp?id=82

-----------------------------------------------------------------------------------------------------------------------------------------

参考:http://bbs.chinaunix.net/thread-116218-1-1.html
          http://blog.csdn.net/fengchaokobe/article/details/6784721
          http://ds.fzu.edu.cn/fine/resources/FlashContent.asp?id=82

中位数之第K小的线性选择算法

1973年,Blum、Floyd等几位大仙合并一体,写了一篇题为 “Time bounds for selection” 的章,给出了一种在数组中选出第k小元素的算法,俗称"中位数之中位数算法"。该算法从理论上保证了最坏情形下的线性时间复杂度(O(n))。而一个简单的排序算法像快速排序的时间复杂度是O(nlogn),利用类似于快速排序的做法是:首先对该无序数组进行排序(O(nlogn)),然后进行一次遍历(O(k))就可以找到第k小元素。下面我们来重点看看中位数排序法。

该算法使用分而治之的策略,查找到第K小元素在最坏情况下的时间复杂度为O(n)。

实现该算法的步骤如下:

1.如果n是一个比较小的数,比如n<6,那么只需要对此无序数组进行排序后,即可很容易的得到第K小元素。

此时约束时间T=7。

2.如果n>5,那么我们将这个无序数组分成五组。此时约束时间T=n/5。

3.找出每组的中位数,构成集合M。此时的约束时间T=7n/5.

4.递归的调用selection(M,|M|/2)算法查找上一步中所有中位数的中位数,设为m。此时的约束时间

T=T(n/5)。

5.用m来分割此时的数组,比较m与其他的(n-1)个数,小于m的数置于左集合L,大于m的数置于右集合R。当

然,中位数m的下标r=|L|+1(|L|是左集合L的个数)。此时的约束时间T=T(n)。

如果r=k,那么返回m。

如果r<k,那么在小于m的左集合L中递归查找第K小数。

如果r>k,那么在大于m的右集合R中递归查找第K小数。

递归方程:T(n)=O(n) + T(n/5) + T(7n/10) (证明过程略)

如果你想知道怎样得到次方程的,不妨找一本关于算法的书看一看或直接给我留言,谢谢!

另外,我想说的是:我为什么分为五组而不是分为其他的组。

假设我们将此数组分为三组,那么有:T(n) = O(n) + T(n/3) + T(2n/3) so T(n) > O(n)。如果我

们将此数组分成五组以上,那么就会显得有些麻烦了,所以分为五个组是最理性的选择。

由于鄙人的翻译水平所致,文中不妥之处还望各位指出来,谢谢!

快速排序以及第k小元素的线性选择算法的更多相关文章

  1. 快速排序-无序数组K小元素

    13:07:382020-03-10 11:16:13 问题描述: 找到一个无序数组中第K小的数 样例 1: 输入: [3, 4, 1, 2, 5], k = 3 输出: 3 样例 2: 输入: [1 ...

  2. 清橙OJ 1082 查找第K小元素 -- 快速排序

    题目地址:http://oj.tsinsen.com/A1082 问题描述 给定一个大小为n的数组s和一个整数K,请找出数组中的第K小元素. 这是一个补充程序的试题,你需要完成一个函数: int fi ...

  3. 算法导论学习之线性时间求第k小元素+堆思想求前k大元素

    对于曾经,假设要我求第k小元素.或者是求前k大元素,我可能会将元素先排序,然后就直接求出来了,可是如今有了更好的思路. 一.线性时间内求第k小元素 这个算法又是一个基于分治思想的算法. 其详细的分治思 ...

  4. 寻找第K小元素

    要在一个序列里找出第K小元素,可以用排序算法,然后再找.可以证明,排序算法的上界为O(nlogn). 在这里,给出两种可以在线性时间内找出第K小元素的方法. 方法1: (1) 选定一个比较小的阈值(如 ...

  5. 中位数与第K小元素

    算法实际上是模仿快速排序算法设计出来的,其基本思想也是对输入数组进行递归划分,与快速排序不同的是,它只对划分出来的子数组之一进行递归处理: int randompartition(int a[],in ...

  6. Ex 2_22 两个有序列表合并后的第k小元素..._第四次作业

    package org.xiu68.ch02; public class Ex2_22 { public static void main(String[] args) { // TODO Auto- ...

  7. 有序矩阵中第k小元素

    有序矩阵中第k小元素 题目: 给定一个 n x n 矩阵,其中每行和每列元素均按升序排序,找到矩阵中第 k 小的元素. 请注意,它是排序后的第 k 小元素,而不是第 k 个不同的元素. 看到有序就会想 ...

  8. 查询无序列表中第K小元素

    当需要在无需列表中寻找第k小的元素时,一个显然的方法是将所有数据进行排序,然后检索k个元素.这种方法的运行时间为O(n log(n)). 无序列表调用分区函数将自身分解成两个子表,其长度为i和n-i. ...

  9. 找出数组[1...n]中第k小元素

    //问题描述: 试编写一个算法,使之能够在数组L[1...n]中找出第k小的元素(即从小到大排序后处于第k个位置的元素) #include <stdio.h> // 结合快排思想,查找第5 ...

随机推荐

  1. Windows用WinDbg分析蓝屏dump文件查找原因(转)

    WinDbg官方下载: http://msdl.microsoft.com/download/symbols/debuggers/dbg_x86_6.11.1.404.msi http://msdl. ...

  2. MC34063组成DC-DC电路

    +VO的输出电压峰值可达2倍V_IN,-VO的输出电压可达-V_IN. 需要注意的是,3路的峰值电路不能超过1.5A,同时两路附加电源的输出功率和必须小于V_IN·I·(1-D), 其中I为主输出的电 ...

  3. WebService如何抛出干净的异常

    转载:http://www.cnblogs.com/ahdung/p/3953431.html 说明:[干净]指的是客户端在捕获WebService(下称WS)抛出的异常时,得到的ex.Message ...

  4. 学习mfc书籍

    Visual C++ and MFC Programming http://www.math.hcmuns.edu.vn/~tatuana/C%20For%20Win/MFC/Tai%20Lieu%2 ...

  5. SharePoint Server 2013 Offline Installation (without Internet)

    转自:http://social.msdn.microsoft.com/Forums/sharepoint/zh-CN/08f90e0f-1f52-4eba-9f6e-4dd635ffaadc/sha ...

  6. 内核镜像zImage是如何生成的

    转载:内核镜像zImage是如何生成的 内核镜像zImage是如何生成的 前面两篇文章介绍了vmlinux 文件生成,这个文件必然是核心的linux内核,但是它是ELF格式的文件,其中包含了可执行的二 ...

  7. Code First 数据库的表中属性的配置

      数据类型的约定配置 默认规则 列的数据类型是由数据库决定的,SqlServer的默认规则如下 String: nvarchar(MAX) Int:int Bool:bit Decimal:deci ...

  8. 配置Android-Annotation (github20大开源:http://www.eoeandroid.com/thread-278980-1-1.html)

    1. 把androidannotations-X.X.X-api.jar 放在libs文件夹 2. 把androidannotations-X.X.X.jar 放在文件夹compile-libs,1与 ...

  9. Linux进程间通信—信号

    三.信号(Signal) 信号是Unix系统中使用的最古老的进程间通信的方法之一.操作系统通过信号来通知某一进程发生了某一种预定好的事件:接收到信号的进程可以选择不同的方式处理该信号,一是可以采用默认 ...

  10. C#常见算法题目

        //冒泡排序    public class bubblesorter    {        public void sort(int[] list)        {            ...