• 【算法导论】学习笔记——第8章 线性时间排序


    本章节主要证明对包含n个元素的输入序列来说,任何比较排序在最坏情况下都要经过omega(nlgn)次比较。从而证明归并排序和堆排序是渐近最优的。同时,介绍了三种线性时间复杂度的排序算法:计数排序、基数排序和桶排序。

    1. 排序算法的下界
    在确定排序算法的下界时,借助决策树模型。决策树模型是一棵完全二叉树,它可以表示在给定输入规模情况下,某一特定排序算法对所有元素的比较操作。对于比较操作,假定元素互异,因此仅使用小于等于和大于比较操作符。典型的决策树模型如下图所示:

    显然,n个元素,n!种不同排列,均可以为叶子结点。因此,有
    n! <= l <= 2^h,(l为从根结点到叶子结点的路径长度,即为比较次数)。
    故h >= lg(n!) = omega(nlgn)。
    8.1-1
    解 叶节点的最小深度为n-1,即有序的排列。

    8.1-3
    证明 (1)因为n!/2 <= l <= 2^h,所以h>=nlgn-nlge-1
         (2)因为n!/n <= l <= 2^h,所以h>=nlgn-nlge-lgn
         (3)因为n!/2^n <= l <= 2^h,所以h>=nlgn-nlge-n
        所以h=omega(nlgn)无法达到线性。

    8.1-4
    证明:先分析题目,n个元素,由n/k个子序列构成,子序列内部无序,但是子序列之间是有序的。因此,对长度为n的序列的排序转化为对n/k个由k个元素构成的子序列进行排序。证明比较次数的下界是omega(nlgn)。
    首先对于k个无序元素进行排序,比较次数的下界为omega(klgk),n/k个子序列的比较次数为omega(nlgk)。
    让后还需要考虑,对n个元素确定这n/k个子序列执行的比较次数。因为k ,不妨令n=km,则
    (k!)^(n/k) <= l <= 2^h,故h>=mlg(k)!,所以h>=omega(mklgk)=omega(nlgk)
    因此总的比较次数为omega(nlgk)。

    2. 计数排序
    计数排序假设n个输入中的每一个都是0到k区间的一个整数,其中k为某个整数。当k=O(n)时,排序的运行时间为theta(n)。
    计数排序的基本思想是:对每一个输入元素x,确定小于x的元素个数。利用这一信息,就可以直接把x放到它在输出数组中的位置了。当存在相同元素时,需要从后向前扫描,当确定好当前元素位置后,需要调整计数个数。代码实现如下:

     1 #define MAXN 105
     2 #define MAXK 100
     3 
     4 int A[MAXN], B[MAXN];   // A[]: original numbers, B[]: sorted numbers.
     5 
     6 void CountingSort(int A[], int B[], int n, int k) {
     7     int C[MAXK+1];
     8     int i, j;
     9 
    10     for (i=0; i<=k; ++i)
    11         C[i] = 0;
    12 
    13     for (j=1; j<=n; ++j)
    14         ++C[A[j]];
    15     // C[i] now contains the number of elements equal to i.
    16 
    17     for (i=1; i<=k; ++i)
    18         C[i] += C[i-1];
    19     // C[i] now contains the number of elements less than or equal to i.
    20 
    21     for (j=n; j>=1; --j) {
    22         B[C[A[j]]] = A[j];
    23         --C[A[j]];
    24     }
    25 }

    8.2-4
    解:就是计数排序中1~19行程序,先计算得到数组C。所求[a..b]区间的个数就是C[b]-C[a-1]。
      时间复杂度T(n) = theta(n) + theta(k) = theta(n+k)。

    3. 基数排序
    基数排序(radix sort),即给定n个d位数,其中每个数位有k个可能的取值。对于基数排序的d次循环的每一次循环,使用复杂度为theta(n+k)的稳定排序(如计数排序),则基数排序的时间复杂度为theta(d(n+k))。代码实现如下:

     1 void CountingSort(int A[], int B[], int n, int k, int in) {
     2     int C[MAXK+1];
     3     int i, j, tmp, mod=pow(k, in), div = mod/k;
     4 
     5     for (i=0; i<k; ++i)
     6         C[i] = 0;
     7 
     8     for (j=1; j<=n; ++j) {
     9         tmp = A[j]%mod/div;
    10         ++C[tmp];
    11     }
    12 
    13     for (i=1; i<k; ++i)
    14         C[i] += C[i-1];
    15 
    16     for (j=n; j>=1; --j) {
    17         tmp = A[j]%mod/div;
    18         B[C[tmp]] = A[j];
    19         --C[tmp];
    20     }
    21 }
    22 
    23 void RadixSort(int A[], int n, int d) {
    24     int i;
    25     int B[MAXN];
    26 
    27     for (i=1; i<=d; ++i) {
    28         if (i & 1)
    29             CountingSort(A, B, n, 10, i);
    30         else
    31             CountingSort(B, A, n, 10, i);
    32     }
    33 
    34     if (d & 1) {
    35         for (i=1; i<=n; ++i)
    36             A[i] = B[i];
    37     }
    38 }
  • 相关阅读:
    Ubuntu VIM下实现python自动缩进
    认识Python和基础知识
    Linux常用服务器搭建
    VIM常用命令
    Linux基础
    LINUX操作系统VIM的安装和配置
    Ubuntu 16.04下安装搜狗输入法
    Ubuntu 16.04下sublime text3安装
    Ubuntu软件安装与卸载
    present(模态)实现出push的效果
  • 原文地址:https://www.cnblogs.com/bombe1013/p/3943957.html
Copyright © 2020-2023  润新知