zoukankan      html  css  js  c++  java
  • 内部排序算法

    http://blog.csdn.net/xiazdong/article/details/8462393
    http://blog.csdn.net/hguisu/article/details/7776068
    注:本文中算法实现来自参考网页2,貌似有错误,仅供思路参考!
     
    排序有内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存。

    我们这里说说排序算法都是内部排序。    

     
    In-place sort(不占用额外内存或占用常数的内存):插入排序、选择排序、冒泡排序、堆排序、快速排序。
    Out-place sort:归并排序、计数排序、基数排序、桶排序。
     
    stable sort:插入排序、冒泡排序、归并排序、计数排序、基数排序、桶排序。
    unstable sort:选择排序、快速排序、堆排序。
    稳定性的好处:排序算法如果是稳定的,那么从一个键上排序,然后再从另一个键上排序,第一个键排序的结果可以为第二个键排序所用。另外,如果排序算法稳定,可以避免多余的比较。
     
    如果对于不稳定的算法进行改进,使得那些不稳定的算法也稳定?
    其实很简单,只需要在每个输入元素加一个index,表示初始时的数组索引,当不稳定的算法排好序后,对于相同的元素对index排序即可。

    1. 直接插入排序(插入排序)

    基本思想:将一个记录插入到已排序好的有序表中,从而得到一个新,记录数增1的有序表。即:先将序列的第1个记录看成是一个有序的子序列,然后从第2个记录逐个进行插入,直至整个序列有序为止。

    要点:设立哨兵,作为临时存储和判断数组边界之用。

    特点:stable sort、In-place sort

    最优复杂度:当输入数组就是排好序的时候,复杂度为O(n),而快速排序在这种情况下会产生O(n^2)的复杂度。
    最差复杂度:当输入数组为倒序时,复杂度为O(n^2)。
    插入排序比较适合用于“少量元素的数组”。
     
    其实插入排序的复杂度和逆序对的个数一样,当数组倒序时,逆序对的个数为n(n-1)/2,因此插入排序复杂度为O(n^2)。
     
    是否能将伪代码第6-8行用二分法实现?
    实际上是不能的。因为第6-8行并不是单纯的线性查找,而是还要移出一个空位让A[i]插入,因此就算二分查找用O(lgn)查到了插入的位置,但是还是要用O(n)的时间移出一个空位。
     
    快速排序(不使用随机化)是否一定比插入排序快?
    答:不一定,当输入数组已经排好序时,插入排序需要O(n)时间,而快速排序需要O(n^2)时间。
     

    示例:

     
    伪代码:
    递归版:
     
    实现:
    void print(int a[], int n ,int i){
        cout<<i <<":";
        for(int j= 0; j<8; j++){
            cout<<a[j] <<" ";
        }
        cout<<endl;
    }
    
    
    void InsertSort(int a[], int n)
    {
        for(int i= 1; i<n; i++){
            if(a[i] < a[i-1]){               //若第i个元素大于i-1元素,直接插入。小于的话,移动有序表后插入
                int j= i-1;    
                int x = a[i];         //复制为哨兵,即存储待排序元素
                a[i] = a[i-1];           //先后移一个元素
                while(x < a[j]){     //查找在有序表的插入位置
                    a[j+1] = a[j];
                    j--;         //元素后移
                }
                a[j+1] = x;         //插入到正确位置
            }
            print(a,n,i);            //打印每趟排序的结果
        }
        
    }
    
    int main(){
        int a[8] = {3,1,5,7,2,4,9,6};
        InsertSort(a,8);
        print(a,8,8);
    }
    View Code
     
    2. 希尔排序(缩小增量排序)(插入排序)

    基本思想:

    先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,待整个序列中的记录“基本有序”时,再对全体记录进行依次直接插入排序。

    操作方法:

    1. 选择一个增量序列t1,t2,…,tk,其中ti>tj,tk=1;
    2. 按增量序列个数k,对序列进行k趟排序;
    3. 每趟排序,根据对应的增量ti,将待排序列分割成若干长度为m的子序列,分别对各子表进行直接插入排序。仅增量因子为1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。

    希尔排序时效分析很难,关键码的比较次数与记录移动次数依赖于增量因子序列d的选取,特定情况下可以准确估算出关键码的比较次数和记录的移动次数。目前还没有人给出选取最好的增量因子序列的方法。增量因子序列可以有各种取法,有取奇数的,也有取质数的,但需要注意:增量因子中除1 外没有公因子,且最后一个增量因子必须为1。希尔排序方法是一个不稳定的排序方法。

    示例:

    实现:

    void print(int a[], int n ,int i){
        cout<<i <<":";
        for(int j= 0; j<8; j++){
            cout<<a[j] <<" ";
        }
        cout<<endl;
    }
    /**
     * 直接插入排序的一般形式
     *
     * @param int dk 缩小增量,如果是直接插入排序,dk=1
     *
     */
    
    void ShellInsertSort(int a[], int n, int dk)
    {
        for(int i= dk; i<n; ++i){
            if(a[i] < a[i-dk]){            //若第i个元素大于i-1元素,直接插入。小于的话,移动有序表后插入
                int j = i-dk;    
                int x = a[i];            //复制为哨兵,即存储待排序元素
                a[i] = a[i-dk];            //首先后移一个元素
                while(x < a[j]){        //查找在有序表的插入位置
                    a[j+dk] = a[j];
                    j -= dk;             //元素后移
                }
                a[j+dk] = x;            //插入到正确位置
            }
            print(a, n,i );
        }
        
    }
    
    /**
     * 先按增量d(n/2,n为要排序数的个数进行希尔排序
     *
     */
    void shellSort(int a[], int n){
    
        int dk = n/2;
        while( dk >= 1  ){
            ShellInsertSort(a, n, dk);
            dk = dk/2;
        }
    }
    int main(){
        int a[8] = {3,1,5,7,2,4,9,6};
        //ShellInsertSort(a,8,1); //直接插入排序
        shellSort(a,8);              //希尔插入排序
        print(a,8,8);
    }
    View Code
    3. 简单选择排序(选择排序)

    基本思想:

    在要排序的一组数中,选出最小(或者最大)的个数与第1个位置的数交换;然后在剩下的数当中再找最小(或者最大)的与第2个位置的数交换,依次类推,直到第n-1个元素(倒数第二个数)和第n个元素(最后个数)比较为止。

    操作方法:

    第一趟,从n 个记录中找出关键码最小的记录与第一个记录交换;

    第二趟,从第二个记录开始的n-1 个记录中再选出关键码最小的记录与第二个记录交换;

    以此类推.....

    第i 趟,则从第i 个记录开始的n-i+1 个记录中选出关键码最小的记录与第i 个记录交换,

    直到整个序列按关键码有序。

    特性:In-place sort,unstable sort。
    思想:每次找一个最小值。
    最好情况时间:O(n^2)。
    最坏情况时间:O(n^2)。
     
    为什么伪代码中第3行只有循环n-1次而不是n次?
    在循环不变式证明中也提到了,如果A[1...n-1]已排序,且包含了A中最小的n-1个元素,则A[n]肯定是最大的,因此肯定是已排序的。
     

    示例:

     

     
    伪代码:
    递归版:
    T(n)=T(n-1)+O(n) => T(n)=O(n^2)
     
    实现:
    void print(int a[], int n ,int i){
        cout<<""<<i+1 <<"趟 : ";
        for(int j= 0; j<8; j++){
            cout<<a[j] <<"  ";
        }
        cout<<endl;
    }
    /**
     * 数组的最小值
     *
     * @return int 数组的键值
     */
    int SelectMinKey(int a[], int n, int i)
    {
        int k = i;
        for(int j=i+1 ;j< n; ++j) {
            if(a[k] > a[j]) k = j;
        }
        return k;
    }
    
    /**
     * 选择排序
     *
     */
    void selectSort(int a[], int n){
        int key, tmp;
        for(int i = 0; i< n; ++i) {
            key = SelectMinKey(a, n,i);           //选择最小的元素
            if(key != i){
                tmp = a[i];  a[i] = a[key]; a[key] = tmp; //最小元素与第i位置元素互换
            }
            print(a,  n , i);
        }
    }
    int main(){
        int a[8] = {3,1,5,7,2,4,9,6};
        cout<<"初始值:";
        for(int j= 0; j<8; j++){
            cout<<a[j] <<"  ";
        }
        cout<<endl<<endl;
        selectSort(a, 8);
        print(a,8,8);
    }
    View Code

    改进:二元选择排序

    简单选择排序,每趟循环只能确定一个元素排序后的定位。我们可以考虑改进为每趟循环确定两个元素(当前趟最大和最小记录)的位置,从而减少排序所需的循环次数。改进后对n个数据进行排序,最多只需进行[n/2]趟循环即可。具体实现如下:

    void SelectSort(int r[],int n) {
        int i ,j , min ,max, tmp;
        for (i=1 ;i <= n/2;i++) {  
            // 做不超过n/2趟选择排序 
            min = i; max = i ; //分别记录最大和最小关键字记录位置
            for (j= i+1; j<= n-i; j++) {
                if (r[j] > r[max]) { 
                    max = j ; continue ; 
                }  
                if (r[j]< r[min]) { 
                    min = j ; 
                }   
          }  
          //该交换操作还可分情况讨论以提高效率
          tmp = r[i-1]; r[i-1] = r[min]; r[min] = tmp;
          tmp = r[n-i]; r[n-i] = r[max]; r[max] = tmp; 
    
        } 
    }
    View Code

    4. 堆排序(选择排序)

    堆排序是一种树形选择排序,是对简单选择排序的有效改进。

    基本思想:

    初始时把要排序的n个数的序列看作是一棵顺序存储的二叉树(一维数组存储二叉树),调整它们的存储序,使之成为一个堆,将堆顶元素输出,得到n 个元素中最小(或最大)的元素,这时堆的根节点的数最小(或者最大)。然后对前面(n-1)个元素重新调整使之成为堆,输出堆顶元素,得到n 个元素中次小(或次大)的元素。依此类推,直到只有两个节点的堆,并对它们作交换,最后得到有n个节点的有序序列。称这个过程为堆排序

    特性:unstable sort、In-place sort。
    最优时间:O(nlgn)
    最差时间:O(nlgn)
    思想:运用了最小堆、最大堆这个数据结构,而堆还能用于构建优先队列。
     
    优先队列应用于进程间调度、任务调度等。
    堆数据结构应用于Dijkstra、Prim算法。
     
    复杂度分析:

    设树深度为k,。从根到叶的筛选,元素比较次数至多2(k-1)次,交换记录至多k 次。所以,在建好堆后,排序过程中的筛选次数不超过下式: 

                                    

    而建堆时的比较次数不超过4n 次,因此堆排序最坏情况下,时间复杂度也为:O(nlogn )。

     
    伪代码:
     
    实现:
    void print(int a[], int n){
        for(int j= 0; j<n; j++){
            cout<<a[j] <<"  ";
        }
        cout<<endl;
    }
    
    
    
    /**
     * 已知H[s…m]除了H[s] 外均满足堆的定义
     * 调整H[s],使其成为大顶堆.即将对第s个结点为根的子树筛选, 
     *
     * @param H是待调整的堆数组
     * @param s是待调整的数组元素的位置
     * @param length是数组的长度
     *
     */
    void HeapAdjust(int H[],int s, int length)
    {
        int tmp  = H[s];
        int child = 2*s+1; //左孩子结点的位置。(i+1 为当前调整结点的右孩子结点的位置)
        while (child < length) {
            if(child+1 <length && H[child]<H[child+1]) { // 如果右孩子大于左孩子(找到比当前待调整结点大的孩子结点)
                ++child ;
            }
            if(H[s]<H[child]) {  // 如果较大的子结点大于父结点
                H[s] = H[child]; // 那么把较大的子结点往上移动,替换它的父结点
                s = child;         // 重新设置s ,即待调整的下一个结点的位置
                child = 2*s+1;
            }  else {             // 如果当前待调整结点大于它的左右孩子,则不需要调整,直接退出
                 break;
            }
            H[s] = tmp;            // 当前待调整的结点放到比其大的孩子结点位置上
        }
        print(H,length);
    }
    
    
    /**
     * 初始堆进行调整
     * 将H[0..length-1]建成堆
     * 调整完之后第一个元素是序列的最小的元素
     */
    void BuildingHeap(int H[], int length)
    { 
        //最后一个有孩子的节点的位置 i=  (length -1) / 2
        for (int i = (length -1) / 2 ; i >= 0; --i)
            HeapAdjust(H,i,length);
    }
    /**
     * 堆排序算法
     */
    void HeapSort(int H[],int length)
    {
        //初始堆
        BuildingHeap(H, length);
        //从最后一个元素开始对序列进行调整
        for (int i = length - 1; i > 0; --i)
        {
            //交换堆顶元素H[0]和堆中最后一个元素
            int temp = H[i]; H[i] = H[0]; H[0] = temp;
            //每次交换堆顶元素和堆中最后一个元素之后,都要对堆进行调整
            HeapAdjust(H,0,i);
      }
    } 
    
    int main(){
        int H[10] = {3,1,5,7,2,4,9,6,10,8};
        cout<<"初始值:";
        print(H,10);
        HeapSort(H,10);
        //selectSort(a, 8);
        cout<<"结果:";
        print(H,10);
    
    }
    View Code

    5. 冒泡排序(交换排序)

    基本思想:

    在要排序的一组数中,对当前还未排好序的范围内的全部数,自上而下对相邻的两个数依次进行比较和调整,让较大的数往下沉,较小的往上冒。即:每当两相邻的数比较后发现它们的排序与排序要求相反时,就将它们互换。

    特点:stable sort、In-place sort
    思想:通过两两交换,像水中的泡泡一样,小的先冒出来,大的后冒出来。
    最坏运行时间:O(n^2)
    最佳运行时间:O(n^2)(当然,也可以进行改进使得最佳运行时间为O(n))
     
    冒泡排序和插入排序哪个更快?
    一般的人回答:“差不多吧,因为渐近时间都是O(n^2)”。
    但是事实上不是这样的,插入排序的速度直接是逆序对的个数,而冒泡排序中执行“交换“的次数是逆序对的个数,“比较”的次数可能更多,因此冒泡排序执行的时间至少是逆序对的个数,因此插入排序的执行时间至少比冒泡排序快。
     

    示例:

     

     
    伪代码:
    递归版:
     
    实现:
    void bubbleSort(int a[], int n){
        for(int i =0 ; i< n-1; ++i) {
            for(int j = 0; j < n-i-1; ++j) {
                if(a[j] > a[j+1])
                {
                    int tmp = a[j] ; a[j] = a[j+1] ;  a[j+1] = tmp;
                }
            }
        }
    }
    View Code
    改进版:
    (1)加flag标志某一趟排序过程中是否有数据交换,如果进行某一趟排序时并没有进行数据交换,则说明数据已经按要求排列好,可立即结束排序,避免不必要的比较过程。
    最佳运行时间:O(n)
    最坏运行时间:O(n^2)
    (2)设置一标志性变量pos,用于记录每趟排序中最后一次进行交换的位置。由于pos位置之后的记录均已交换到位,故在进行下一趟排序时只要扫描到pos位置即可。
    void Bubble_1 ( int r[], int n) {
        int i= n -1;  //初始时,最后位置保持不变
        while ( i> 0) { 
            int pos= 0; //每趟开始时,无记录交换
            for (int j= 0; j< i; j++)
                if (r[j]> r[j+1]) {
                    pos= j; //记录交换的位置 
                    int tmp = r[j]; r[j]=r[j+1];r[j+1]=tmp;
                } 
            i= pos; //为下一趟排序作准备
         } 
    }  
    View Code

    (3)在每趟排序中进行正向和反向两遍冒泡的方法一次可以得到两个最终值(最大者和最小者) , 从而使排序趟数几乎减少了一半。

    void Bubble_2 ( int r[], int n){
        int low = 0; 
        int high= n -1; //设置变量的初始值
        int tmp,j;
        while (low < high) {
            for (j= low; j< high; ++j) //正向冒泡,找到最大者
                if (r[j]> r[j+1]) {
                    tmp = r[j]; r[j]=r[j+1];r[j+1]=tmp;
                } 
            --high;                    //修改high值, 前移一位
            for ( j=high; j>low; --j) //反向冒泡,找到最小者
                if (r[j]<r[j-1]) {
                    tmp = r[j]; r[j]=r[j-1];r[j-1]=tmp;
                }
            ++low;                    //修改low值,后移一位
        } 
    } 
    View Code

    6. 快速排序(交换排序)

    基本思想:

    1)选择一个基准元素,通常选择第一个元素或者最后一个元素,

    2)通过一趟排序讲待排序的记录分割成独立的两部分,其中一部分记录的元素值均比基准元素值小。另一部分记录的 元素值比基准值大。

    3)此时基准元素在其排好序后的正确位置

    4)然后分别对这两部分记录用同样的方法继续进行排序,直到整个序列有序。

    特性:unstable sort、In-place sort。
    最坏运行时间:当输入数组已排序时,时间为O(n^2),当然可以通过随机化来改进(shuffle array 或者 randomized select pivot),使得期望运行时间为O(nlgn)。
    最佳运行时间:O(nlgn)
    快速排序的思想也是分治法。
    当输入数组的所有元素都一样时,不管是快速排序还是随机化快速排序的复杂度都为O(n^2)。
     
    注意:只要partition的划分比例是常数的,则快排的效率就是O(nlgn),比如当partition的划分比例为10000:1时(足够不平衡了),快排的效率还是O(nlgn)
     
    枢轴元素的选择:通常以“三者取中法”来选取基准记录,即将排序区间的两个端点与中点三个记录关键码居中的调整为支点记录。
     
    改进方法:
    (1)采取三路划分方法,将小于枢轴元素的元素放到数组最左边,将等于枢轴元素的元素放到数组中间,将大于枢轴元素的元素放到数组最右边。下次只处理左边部分和右边部分,中间部分不再参与下一轮排序。
    这个方法不能完全满足只扫描一次的要求,但它有两个好处:首先,如果数据中没有重复的值,那么该方法几乎没有额外的开销;其次,如果有重复值,那么这些重复的值不会参与下一趟排序,减少了无用的划分。

    (2)只对长度大于k的子序列递归调用快速排序,让原序列基本有序,然后再对整个基本有序序列用插入排序算法排序。

     
    示例:

    伪代码:

    随机化partition的实现:
     
    改进当所有元素相同时的效率的Partition实现:
     
    实现:
    void print(int a[], int n){
        for(int j= 0; j<n; j++){
            cout<<a[j] <<"  ";
        }
        cout<<endl;
    }
    
    void swap(int *a, int *b)
    {
        int tmp = *a;
        *a = *b;
        *b = tmp;
    }
    
    int partition(int a[], int low, int high)
    {
        int privotKey = a[low];                                //基准元素
        while(low < high){                                    //从表的两端交替地向中间扫描
            while(low < high  && a[high] >= privotKey) --high;  //从high 所指位置向前搜索,至多到low+1 位置。将比基准元素小的交换到低端
            swap(&a[low], &a[high]);
            while(low < high  && a[low] <= privotKey ) ++low;
            swap(&a[low], &a[high]);
        }
        print(a,10);
        return low;
    }
    
    
    void quickSort(int a[], int low, int high){
        if(low < high){
            int privotLoc = partition(a,  low,  high);  //将表一分为二
            quickSort(a,  low,  privotLoc -1);            //递归对低子表递归排序
            quickSort(a,   privotLoc + 1, high);        //递归对高子表递归排序
        }
    }
    
    int main(){
        int a[10] = {3,1,5,7,2,4,9,6,10,8};
        cout<<"初始值:";
        print(a,10);
        quickSort(a,0,9);
        cout<<"结果:";
        print(a,10);
    
    }
    View Code

    7. 归并排序(Merge)

    基本思想:

    归并排序法是将两个(或两个以上)有序表合并成一个新的有序表,即把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。

    特点:stable sort、Out-place sort
    思想:运用分治法思想解决排序问题。
    最坏情况运行时间:O(nlgn)
    最佳运行时间:O(nlgn)
     
    分治法介绍:分治法就是将原问题分解为多个独立的子问题,且这些子问题的形式和原问题相似,只是规模上减少了,求解完子问题后合并结果构成原问题的解。
    分治法通常有3步:Divide(分解子问题的步骤) 、 Conquer(递归解决子问题的步骤)、 Combine(子问题解求出来后合并成原问题解的步骤)。
    假设Divide需要f(n)时间,Conquer分解为b个子问题,且子问题大小为a,Combine需要g(n)时间,则递归式为:
    T(n)=bT(n/a)+f(n)+g(n)
    就如归并排序,Divide的步骤为m=(p+q)/2,因此为O(1),Combine步骤为merge()函数,Conquer步骤为分解为2个子问题,子问题大小为n/2,因此:
    归并排序的递归式:T(n)=2T(n/2)+O(n)
     
    而求解递归式的三种方法有:
    (1)替换法:主要用于验证递归式的复杂度。
    (2)递归树:能够大致估算递归式的复杂度,估算完后可以用替换法验证。
    (3)主定理:用于解一些常见的递归式。
     
    归并排序的缺点是什么?
    答:他是Out-place sort,因此相比快排,需要很多额外的空间。
     
    问:为什么归并排序比快速排序慢?
    答:虽然渐近复杂度一样,但是归并排序的系数比快排大。
     
    问:对于归并排序有什么改进?
    答:就是在数组长度为k时,用插入排序,因为插入排序适合对小数组排序。复杂度为O(nk+nlg(n/k)) ,当k=O(lgn)时,复杂度为O(nlgn)
     

    示例:

     

     
    伪代码:
     
    实现:
    void print(int a[], int n){
        for(int j= 0; j<n; j++){
            cout<<a[j] <<"  ";
        }
        cout<<endl;
    }
    
    //将r[i…m]和r[m +1 …n]归并到辅助数组rf[i…n]
    void Merge(ElemType *r,ElemType *rf, int i, int m, int n)
    {
        int j,k;
        for(j=m+1,k=i; i<=m && j <=n ; ++k){
            if(r[j] < r[i]) rf[k] = r[j++];
            else rf[k] = r[i++];
        }
        while(i <= m)  rf[k++] = r[i++];
        while(j <= n)  rf[k++] = r[j++];
        print(rf,n+1);
    }
    
    void MergeSort(ElemType *r, ElemType *rf, int lenght)
    { 
        int len = 1;
        ElemType *q = r ;
        ElemType *tmp ;
        while(len < lenght) {
            int s = len;
            len = 2 * s ;
            int i = 0;
            while(i+ len <lenght){
                Merge(q, rf,  i, i+ s-1, i+ len-1 ); //对等长的两个子表合并
                i = i+ len;
            }
            if(i + s < lenght){
                Merge(q, rf,  i, i+ s -1, lenght -1); //对不等长的两个子表合并
            }
            tmp = q; q = rf; rf = tmp; //交换q,rf,以保证下一趟归并时,仍从q 归并到rf
        }
    }
    
    
    int main(){
        int a[10] = {3,1,5,7,2,4,9,6,10,8};
        int b[10];
        MergeSort(a, b, 10);
        print(b,10);
        cout<<"结果:";
        print(a,10);
    
    }
    View Code

    8. 计数排序

    特性:stable sort、out-place sort。
    最坏情况运行时间:O(n+k)
    最好情况运行时间:O(n+k)
    当k=O(n)时,计数排序时间为O(n)
     
    伪代码: 

    9. 基数排序

    本文假定每位的排序是计数排序。
    特性:stable sort、Out-place sort。
    最坏情况运行时间:O((n+k)d)
    最好情况运行时间:O((n+k)d)
    d为位数,k为基数(种类数),n为元素个数。
    当d为常数,k=O(n)时,效率为O(n)。
     
    我们也不一定要一位一位排序,我们可以多位多位排序,比如一共10位,我们可以先对低5位排序,再对高5位排序。
    引理:假设n个b位数,将b位数分为多个单元,且每个单元为r位,那么基数排序的效率为O[(b/r)(n+2^r)]。
    当b=O(nlgn),r=lgn时,基数排序效率O(n)
    例:说明如何在O(n)时间内,对0~n^2-1之间的n个整数排序?
    答案:将这些数化为2进制,位数为lg(n^2)=2lgn=O(lgn),因此利用引理,b=O(lgn),而我们设r=lgn,则基数排序可以在O(n)内排序。
     
    基数排序的例子:
     
     
    实现:
    Void RadixSort(Node L[],length,maxradix)
    {
       int m,n,k,lsp;
       k=1;m=1;
       int temp[10][length-1];
       Empty(temp); //清空临时空间
       while(k<maxradix) //遍历所有关键字
       {
         for(int i=0;i<length;i++) //分配过程
        {
           if(L[i]<m)
              Temp[0][n]=L[i];
           else
              Lsp=(L[i]/m)%10; //确定关键字
           Temp[lsp][n]=L[i];
           n++;
       }
       CollectElement(L,Temp); //收集
       n=0;
       m=m*10;
      k++;
     }
    }
    View Code

     

    10. 桶排序
    假设输入数组的元素都在[0,1)之间。
    特性:out-place sort、stable sort。
    最坏情况运行时间:当分布不均匀时,全部元素都分到一个桶中,则O(n^2),当然也可以将插入排序换成堆排序、快速排序等,这样最坏情况就是O(nlgn)。
    最好情况运行时间:O(n)
     
    假定:输入是由一个随机过程产生的[0, 1)区间上均匀分布的实数。将区间[0, 1)划分为n个大小相等的子区间(桶),每桶大小1/n:[0, 1/n), [1/n, 2/n), [2/n, 3/n),…,[k/n, (k+1)/n ),…将n个输入元素分配到这些桶中,对桶中元素进行排序,然后依次连接桶输入0 ≤A[1..n] <1辅助数组B[0..n-1]是一指针数组,指向桶(链表)。
     
    桶排序的例子:
     
    伪代码:
     

     

  • 相关阅读:
    【转】Testing, Thought, and Observations
    【转】Yet another software testing pyramid
    【转】Automated Testing and the Test Pyramid
    Environment setting up troubleshoot.
    [转] Spring相关
    流水账
    Abbreviation
    chrome中文本框样式问题
    Intellij IDEA 使用总结
    限制
  • 原文地址:https://www.cnblogs.com/argenbarbie/p/5398863.html
Copyright © 2011-2022 走看看