算法排序(C++)

来源:互联网 发布:nginx官网 编辑:程序博客网 时间:2024/06/05 13:57


问题:给定一个整数序列,按照从小到大的顺序(确切的说是非递减顺序)排序

输入:一个整数序列
输出:排过序的整数序列

1,插入排序

1.1 直接插入排序

插入排序思想:插入排序是在一个已经有序的小序列基础上,依次插入一个元素进行排序,当然刚开始是一个元素,需要针对第二个元素直到末位元素依次跟前边的元素判断

分析:如果整数序列完全无序--数组完全逆序,插入第二个元素,依次需要比较1个元素,插入第三个元素需要比较2个元素,插入第n个元素,依次需要比较n-1个元素,也就是最坏
情况下需要比较1+2+3+...+(n-1),等差数列求和,为n^2/2,所以最坏情况时间复杂度为O(n^2),最好情况下,数组序列为有序,只需比较n次,复杂度为O(n);空间复杂度为常量O(1)
稳定性:如果碰见一个和插入元素相等的,那么插入元素把想插入的元素放在相等元素的后面。所以,相等元素的前后顺序没有改变,从原无序序列出去的顺序就是排好序后的顺序,所以插入排序是稳定的

代码

#include <iostream>using namespace std;// swap functionvoid swap(int array[],int i,int j){   int tmp=array[i];   array[i]=array[j];   array[j]=tmp;}/*insert sort */void InsertSort(int array[],int n){   for(int i=1;i<n;i++)      {         for(int j=i;j>=0;j--)            {               if(array[j]<array[j-1])                  swap(array,j-1,j);               else                  break;            }      }}int main(){   int n;   cin>>n;// or int a[5]={3,1,4,6,2}   int *a=new int[n];   for(int i=0;i<n;i++)      cin>>a[i];   InsertSort(a,n);   for(int i=0;i<n;i++)      cout<<a[i];   delete []a;}

1.2 折半插入排序

先查找待插入位置,挪动元素,最后插入元素。可以减少比较次数,移动次数不变。所以时间复杂度仍然为O(n^2)

代码

#include <iostream>using namespace std;void BInsertSort(int array[],int n){  for (int i=1;i<n;i++)    {      int low=0,high=i,m=0;      if (array[i]<array[i-1]) // 如果第i个元素小于i-1个,进行排序操作,否则处理下个元素        {          int x=array[i]; // 暂存array[i]          //array[i]=array[i-1];          while(low<=high) //查找待插入的位置            {              m=(low+high)/2;              if (x<array[m]) high=m-1;              else low=m+1;            }          for(int j=i-1;j>=m-1;j--) // 挪动插入位置后边元素            array[j+1]=array[j];          array[m]=x; // 将数据x插入        }             }}int main(){  int a[5]={3,1,4,6,2};  BInsertSort(a,5);  for (int i=0;i<5;i++)    cout<<a[i];}

1.3,希尔插入排序

希尔排序是1959 年由D.L.Shell 提出来的,相对直接排序有较大的改进。希尔排序又叫缩小增量排序

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

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

这里, 我们简单处理增量序列:增量序列d = {n/2 ,n/4, n/8 .....1} n为要排序数的个数

即:先将要排序的一组记录按某个增量d(n/2,n为要排序数的个数)分成若干组子序列,每组中记录的下标相差d.对每组中全部元素进行直接插入排序,
然后再用一个较小的增量(d/2)对它进行分组,在每组中再进行直接插入排序。继续不断缩小增量直至为1,最后使用直接插入排序完成排序


排序的10个元素为49, 38, 65, 97, 26, 13, 27, 49, 55, 4
第一次 gap = 10 / 2 = 5    
49, 38, 65, 97, 26, 13, 27, 49, 55, 4
1A                  1B
    2A                  2B
        3A                  3B
            4A                  4B
                5A                  5B
13  27  49  55  4  49  38  65  97  26    // 排序后
说明,1A,1B,2A,2B等为分组标记,数字相同的表示在同一组,大写字母表示是该组的第几个元素, 每次对同一组的数据进行直接插入排序。
即分成了五组(49, 13) (38, 27) (65, 49)  (97, 55)  (26, 4)这样每组排序后就变成了(13, 49)  (27, 38)  (49, 65)  (55, 97)  (4, 26)

第二次 gap = 5 / 2 = 2
针对第一次排序后的序列进行排序
13  27  49  55  4  49  38  65  97  26
1A      1B      1C     1D      1E     // 内部排序
    2A      2B     2C      2D      2E    
4   26  13  27  38 49  49  55  97  65 // 排序后

第三次 gap = 2 / 2 = 1
4   26  13  27  38 49  49  55  97  65
1A  1B  1C  1D  1E 1F  1G  1H  1I  1J
4   13  26  27  38 49  49  55  65  97 // 排序后
第四次 gap = 1 / 2 = 0 排序完成得到数组,已经有序                              
4   13  26  27  38 49  49  55  65  97

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

实现:

#include <iostream>using namespace std;void shellsort(int array[],int n){  int i,j,gap;  for(gap=n/2;gap>0;gap /=2) // 从数组第gap个元素开始    for(i=0;i<gap;i++) // 每个元素与自己"组内"的数据进行直接插入排序     {      for(j=gap+i;j<n;j +=gap)        if(array[j]<array[j-gap])        {          int tmp=array[j];          int k=j-gap;          while(k>=0 && array[k]>tmp)          {            array[k+gap]=array[k];            k -=gap;          }          array[k+gap]=tmp;        }    }}int main(){  int a[10]={49, 38, 65, 97, 26, 13, 27, 49, 55, 4};  shellsort(a,10);  for (int i=0;i<10;i++)    cout<<a[i]<<',';  cout<<endl;}


2,选择排序

2.1 简单选择排序

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

比如序列:

49, 38, 65, 97, 26, 13, 27, 49, 55, 4

第一趟排序过程:
依次查找38, 65, 97, 26, 13, 27, 49, 55, 4找到元素4最小并且比49小,就跟第一个元素49替换,排序后元素为
4, 38, 65, 97, 26, 13, 27, 49, 55, 49
...

分析:

平均时间复杂度:O(n2)

空间复杂度:O(1)  (用于交换和记录索引)

稳定性:不稳定 (比如序列【3, 3, 2】第一趟就将第一个[3]与[2]交换,导致第一个3挪动到第二个3后面)


#include <iostream>using namespace std;void SelectSort(int array[],int n){  for(int i=0;i<n-1;i++)  {    int k=i;    // 查找交换的位置    for(int j=i+1;j<n;j++)    {      if(array[j]<array[k])        k=j;    }    // 交换    if(k != i)    {      int tmp=array[i];      array[i]=array[k];      array[k]=tmp;    }  }}int main(){  int a[10]={49, 38, 65, 97, 26, 13, 27, 49, 55, 4};  SelectSort(a,10);  for(int i=0;i<10;i++)    cout<<a[i]<<',';  cout<<endl;}

2.2 堆排序

二叉堆的定义

二叉堆是完全二叉树或者是近似完全二叉树。

二叉堆满足二个特性:
1.父结点的键值总是大于或等于(小于或等于)任何一个子节点的键值。
2.每个结点的左子树和右子树都是一个二叉堆(都是最大堆或最小堆)。
当父结点的键值总是大于或等于任何一个子节点的键值时为最大堆。当父结点的键值总是小于或等于任何一个子节点的键值时为最小堆

堆的存储
一般都用数组来表示堆,i结点的父结点下标就为(i – 1) / 2。它的左右子结点下标分别为2 * i + 1和2 * i + 2。如第0个结点左右子结点下标分别为1和2

堆排序主要分为两个过程:

(1)先使长度为N数组形成一个N个节点组成的大顶堆

(2)然后将堆顶数据与末尾数据交换,再对N-1长的堆调整为大顶堆;反复进行,直到堆节点数为1结束堆排序

比如,数组a[]={16,7,3,20,17,8},对其进行堆排序。

先根据该数组元素构建一个完全二叉树,得到


然后调整如下:

第一步找到中间节点3,调整3,8;第二部,找到节点7,查找根节点最大值,替换7跟20,最后找到根节点,调整根节点16和第二步骤中的节点20

最后一步需要重点注意,交换万根节点之后,还需要针对下边的节点再次进行一轮判断

每次调整都是从父节点、左孩子节点、右孩子节点三者中选择最大者跟父节点进行交换(交换之后可能造成被交换的孩子节点不满足堆的性质,因此每次交换之后要重新对被交换的孩子节点进行调整)

这样救得到了初始堆,


然后调整堆顶跟最后一个元素


再次进行堆的调整,替换堆顶和倒数第二个元素.....

算法分析:

平均时间复杂度:O(nlog2n)

空间复杂度:O(1) (用于交换数据) 

稳定性:不稳定

时间复杂度分析:

堆排序的时间复杂度,主要在初始化堆过程和每次选取最大数后重新建堆的过程;

        初始化建堆过程时间:O(n)

        推算过程:

        首先要理解怎么计算这个堆化过程所消耗的时间,可以直接画图去理解;

        假设高度为k,则从倒数第二层右边的节点开始,这一层的节点都要执行子节点比较然后交换(如果顺序是对的就不用交换);倒数第三层呢,则会选择其子节点进行比较和交换,如果没交换就可以不用再执行下去了。如果交换了,那么又要选择一支子树进行比较和交换;

        那么总的时间计算为:s = 2^( i - 1 )  *  ( k - i );其中 i 表示第几层,2^( i - 1) 表示该层上有多少个元素,( k - i) 表示子树上要比较的次数,如果在最差的条件下,就是比较次数后还要交换;因为这个是常数,所以提出来后可以忽略;

        S = 2^(k-2) * 1 + 2^(k-3)*2.....+2*(k-2)+2^(0)*(k-1)  ===> 因为叶子层不用交换,所以i从 k-1 开始到 1;

        这个等式求解,我想高中已经会了:等式左右乘上2,然后和原来的等式相减,就变成了:

        S = 2^(k - 1) + 2^(k - 2) + 2^(k - 3) ..... + 2 - (k-1)

        除最后一项外,就是一个等比数列了,直接用求和公式:S = {  a1[ 1-  (q^n) ] }  / (1-q);

        S = 2^k -k -1;又因为k为完全二叉树的深度,所以 (2^k) <=  n < (2^k  -1 ),总之可以认为:k = logn (实际计算得到应该是 log(n+1) < k <= logn );

        综上所述得到:S = n - longn -1,所以时间复杂度为:O(n)

        更改堆元素后重建堆时间:O(nlogn)

        推算过程:

       1、循环  n -1 次,每次都是从根节点往下循环查找,所以每一次时间是 logn,总时间:logn(n-1) = nlogn  - logn ;

       综上所述:堆排序的时间复杂度为:O(nlogn)

代码:

#include <iostream>using namespace std;//交换data1和data2所指向的整形void DataSwap(int* data1, int* data2){        int temp = *data1;        *data1 = *data2;        *data2 = temp;}/******************************************************** * *函数名称:SlipDown * *参数说明:pDataArray 无序数组; * *               iCurNode为堆中需要调整的节点 * *          iDataNum为数组长度 * *函数返回:分割后的分割数位置 * *函数说明:调整iCurNode处的节点,形成大顶堆     * *********************************************************/void SlipDown(int *pDataArray,int iCurNode,int iDataNum){        int temp = pDataArray[iCurNode];    //记录需要调整的节点值        for (int iNextNode = iCurNode*2; iNextNode <= iDataNum; iNextNode = iCurNode*2) // 防止交换完堆顶元素之后,子堆不符合要求,还要对子节点进行判断        {                if (iNextNode + 1 <= iDataNum                         && pDataArray[iNextNode] < pDataArray[iNextNode + 1])    //寻找iCurNode子节点中的大者                        iNextNode++;                if (pDataArray[iNextNode] > temp)    //大的值上移                        pDataArray[iCurNode] = pDataArray[iNextNode];                    else    //结束调整                        break;                iCurNode = iNextNode;    //更新需要调整的节点        }        pDataArray[iCurNode] = temp;}/******************************************************** * *函数名称:HeapSort * *参数说明:pDataArray 无序数组; * *               iDataNum为无序数据个数 * *说明:    堆排序 * *********************************************************/void HeapSort(int* pDataArray, int iDataNum){        pDataArray--;    //让原先数组下标0对应1,便于堆中节点的访问      for (int i = iDataNum/2; i > 0; i--)    //调整为大顶堆              SlipDown(pDataArray, i, iDataNum);//                SlipDown(pDataArray, 1, iDataNum);        for (int i = iDataNum; i > 1; i--)    //根据大顶堆进行排序        {                DataSwap(&pDataArray[i], &pDataArray[1]);                SlipDown(pDataArray, 1, i - 1);        }}int main(){  int a[10]={49, 38, 65, 97, 26, 13, 27, 49, 55, 4};  HeapSort(a,10);  for(int i=0;i<10;i++)    cout<<a[i]<<',';  cout<<endl;}

3,交换排序

3.1 冒泡排序

冒泡排序:依次比较相邻的数据,将小数据放在前,大数据放在后;即第一趟先比较第1个和第2个数,大数在后,小数在前,再比较第2个数与第3个数,大数在后,小数在前,以此类推则将最大的数"滚动"到最后一个位置;第二趟则将次大的数滚动到倒数第二个位置......第n-1(n为无序数据的个数)趟即能完成排序

比如:


分析:

平均时间复杂度:O(n2)
空间复杂度:O(1)  (用于交换)
稳定性:稳定

优化:代码里边加入了第i此排序进行判断的逻辑,防治提早已经是有序的了

#include <iostream>using namespace std;void DataSwap(int* d1,int* d2){  int tmp= *d1;  *d1=*d2;  *d2=tmp;}void ButtleSort(int array[],int n){  for(int i=0;i<n-1;i++)  {    bool flag=false;  // 标识,用来判断是否在第i唐排序中已经是有序    for(int j=0;j<n-i-1;j++)    {      if(array[j]>array[j+1])        {          flag=true;          DataSwap(&array[j],&array[j+1]);        }    }    // 如果已经有序跳出循环    if(!flag)      break;  }}int main(){  int a[10]={49, 38, 65, 97, 26, 13, 27, 49, 55, 4};  ButtleSort(a,10);  for(int i=0;i<10;i++)    cout<<a[i]<<',';  cout<<endl;}

3.2 快速排序

快速排序由C. A. R. Hoare在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

        说说我的基本思路:每次都取数组的第一个元素作为比较标准(哨兵元素),凡是大于这个哨兵元素的都放在它的右边,凡是小于这个哨兵元素的都放在它的左边;
        大概的步骤:

        1、判断参数条件,其实这是递归的出口;
        2、以数组的第一个元素为哨兵元素,让其他元素和它比较大小;
        3、开始从数组尾部往前循环得到一个小于哨兵元素的  元素A ,把该  元素A  放到第一个元素位置(也就是哨兵元素位置上,因为哨兵元素位置是空的);(这时候要记住 元素A  的位置是空的了)
        4、开始从数组头部往后循环得到一个大于哨兵元素的   元素B ,把该  元素B  放在上一步中移出的  元素A  的位置上;
        5、依次循环上面3、4步,直到最后一个元素为止,那么最后一个元素就存放哨兵元素了。

        6、把小于哨兵元素的那一部分和大于哨兵元素的那一部分分别递归调用本函数,依次递归排序好所有元素;

对应示例一

 也可以将哨兵元素暂存,依次交换元素A和B,最后一个元素交换哨兵,对应实例2

分析

平均时间复杂度:O(nlog2n)
空间复杂度:O(n)
稳定性:不稳定

例子1

//第一个参数要排的数组,第二个参数第一个数,第三个参数数组成员个数void kuaipai(int array[],int low,int hight){int i,j,t,m;if(low<hight){i=low;j=hight;t=array[low];//第一个数为轴while(i<j){while(i<j && array[j]>t)//从右边找出小于轴的数j--;if(i<j)//将小于轴的数array[j]放到左边array[i]的位置{m=array[i];array[i]=array[j];array[j]=m;i++;}while(i<j && array[i]<=t)//从左边找出大于轴的数i++;if(i<j)//将大于轴的数array[i]放在右边array[j]的位置{m=array[j];array[j]=array[i];array[i]=m;j--;}}array[i]=t;//轴放在中间,现在就有两个区域了分别是[0 i-1]和[i+1 hight],分别快排kuaipai(array,0,i-1);kuaipai(array,i+1,hight);}}

实例2

#include <iostream>using namespace std;void quicksort(int a[],int left,int right){    int i,j,t,temp;    if(left>right)       return;    temp=a[left];    i=left;    j=right;    while(i!=j)    {                  //顺序很重要,要先从右边开始找                  while(a[j]>=temp && i<j)                            j--;                   //再找右边的                   while(a[i]<=temp && i<j)                            i++;                   //交换两个数在数组中的位置                   if(i<j)                   {                            t=a[i];                            a[i]=a[j];                            a[j]=t;                   }    }    //最终将基准数归位    a[left]=a[i];    a[i]=temp;    quicksort(a,left,i-1);//继续处理左边的,这里是一个递归的过程    quicksort(a,i+1,right);//继续处理右边的 ,这里是一个递归的过程}int main(){  int a[10]={49, 38, 65, 97, 26, 13, 27, 49, 55, 4};  quicksort(a,0,9);  for(int i=0;i<10;i++)    cout<<a[i]<<',';  cout<<endl;}

4 归并排序

归并排序(MERGE-SORT)是利用归并的思想实现的排序方法,该算法采用经典的分治(divide-and-conquer)策略(分治法将问题分(divide)成一些小的问题然后递归求解,而治(conquer)的阶段则将分的阶段得到的各答案"修补"在一起,即分而治之)。把待排序序列分为若干个子序列,每个子序列是有序的。然后再把有序子序列合并为整体有序序列。


 设r[i…n]由两个有序子表r[i…m]和r[m+1…n]组成,两个子表长度分别为n-i +1、n-m。

    j=m+1;k=i;i=i; //置两个子表的起始下标及辅助数组的起始下标
    若i>m 或j>n,转⑷ //其中一个子表已合并完,比较选取结束
    //选取r[i]和r[j]较小的存入辅助数组rf
    如果r[i]<r[j],rf[k]=r[i]; i++; k++; 转⑵
    否则,rf[k]=r[j]; j++; k++; 转⑵
    //将尚未处理完的子表中元素存入rf
    如果i<=m,将r[i…m]存入rf[k…n] //前一子表非空
    如果j<=n ,  将r[j…n] 存入rf[k…n] //后一子表非空
    合并结束
    
一次排序为例,具体点的:
array                                temp
【(38 49 65 97)(13 27 76)】     {13}    // 两个指针分别指向array的两个子序列,13比38小,13插入temp数组,j++
【(38 49 65 97)(13 27 76)】     {13,27}    // 27比38小,27插入temp数组j++
【(38 49 65 97)(13 27 76)】     {13,27,38}    // 36比76小,38插入temp数组 i++
直到所有元素都插入temp数组,然后复制temp数据到array数组

分析:

时间效率: O(nlog2n)
空间效率: O(n)
稳定性: 稳定

具体算法:

#include <iostream>using namespace std;//将数组 a[low,mid] 与 a(mid,high] 合并(归并)void Merge(int * a, int low, int mid, int high, int * temp){    int i,j,k;    i = low;    j = mid + 1;//避免重复比较a[mid]    k = 0;    while (i <= mid && j <= high)//数组a[low,mid]与数组(mid,high]均没有全部归入数组temp中去    {        if(a[i] <= a[j])        //如果a[i]小于等于a[j]            temp[k++] = a[i++]; //则将a[i]的值赋给temp[k],之后i,k各加一,表示后移一位        else            temp[k++] = a[j++]; //否则,将a[j]的值赋给temp[k],j,k各加一    }    while(i <= mid)             //表示数组a(mid,high]已经全部归入temp数组中去了,而数组a[low,mid]还有剩余        temp[k++] = a[i++];     //将数组a[low,mid]剩下的值,逐一归入数组temp    while(j <= high)           //表示数组a[low,mid]已经全部归入到temp数组中去了,而数组(mid,high]还有剩余        temp[k++] = a[j++];     //将数组a(mid,high]剩下的值,逐一归入数组temp    for (i = 0; i < k; i++)     //将归并后的数组的值逐一赋给数组a[low,high]        a[low+i] = temp[i];     //注意,应从a[low+i]开始赋值}//二路归并(递归实现)void MergeSort(int * a, int low, int high, int * temp){    if (low < high)    {        int mid = (low + high)/2;        MergeSort(a,low,mid,temp);      //左边有序        MergeSort(a,mid+1,high,temp);   //右边有序        Merge(a,low,mid,high,temp);     //再将两个有序序列合并    }}/*----------测试代码----------*/int main(){    int a[] = {49, 38, 65, 97, 26, 13, 27, 49, 55, 4};    int La = sizeof(a)/sizeof(a[0]);    int * p = new int[La];    MergeSort(a,0,La-1,p);    for (int i = 0; i < La; i++)    {        cout<<a[i]<<' ';    }    cout<<endl;    delete []p;}


5 基数排序

基数排序的主要思路是,将所有待比较数值(注意,必须是正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始, 依次进行一次稳定排序,这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列

比如

10 15 31 23 15

LSD逻辑--后边介绍
0 10
1 31
2
3 23
4
5 15 15
第一次结果数据为:
10 31 23 15 15
第二次按十位数值分配
0
1 10 15 15
2 23
3 31
第二次结果数据为:
10 15 15 23 31


基数排序分为LSD(Least significant digital)或MSD(Most significant digital),
LSD的排序方式由数值的最右边(低位)开始,而MSD则相反,由数值的最左边(高位)开始。
注意一点:LSD的基数排序适用于位数少的数列,如果位数多的话,使用MSD的效率会比较好。
MSD的方式与LSD相反,是由高位数为基底开始进行分配,但在分配之后并不马上合并回一个数组中,而是在每个“桶子”中建立“子桶”,将每个桶子中的数值按照下一数位的值分配到“子桶”中。
在进行完最低位数的分配后再合并回单一的数组中。

(2)我们把扑克牌的排序看成由花色和面值两个数据项组成的主关键字排序。


要求如下:
花色顺序:梅花<方块<红心<黑桃
面值顺序:2<3<4<...<10<J<Q<K<A
那么,若要将一副扑克牌排成下列次序:
梅花2,...,梅花A,方块2,...,方块A,红心2,...,红心A,黑桃2,...,黑桃A。

有两种排序方法:
<1>先按花色分成四堆,把各堆收集起来;然后对每堆按面值由小到大排列,再按花色从小到大按堆收叠起来。----称为"最高位优先"(MSD)法。
<2>先按面值由小到大排列成13堆,然后从小到大收集起来;再按花色不同分成四堆,最后顺序收集起来。----称为"最低位优先"(LSD)法

分析:

【时间复杂度】

 平均情况:O(d(n+r))

 因为每一趟分配的时间开销是O(n),收集的开销是O(r),因此执行d趟的时间开销为O(d(n+r)),通常d,r为常数

 最好情况:O(d(n+r))

 最坏情况:O(d(n+r))

【空间复杂度】O(n+r)

【稳定性】稳定

【优点】

    稳定排序;时间复杂度可以突破基于关键字比较排序法的下界O(n)

【缺点】

   需要额外的辅助空间

LSD代码:

#include <iostream>using namespace std;/** 打印数组*/void printArray(int array[],int length){    for (int i = 0; i < length; ++i)    {        cout << array[i] << " ";    }    cout << endl;}/**求数据的最大位数,决定排序次数*/int maxbit(int data[], int n){    int d = 1; //保存最大的位数    int p = 10;    for(int i = 0; i < n; ++i)    {        while(data[i] >= p)        {            p *= 10;            ++d;        }    }    return d;}void radixsort(int data[], int n) //基数排序{    int d = maxbit(data, n);    int tmp[n];    int count[10]; //计数器    int i, j, k;    int radix = 1;    for(i = 1; i <= d; i++) //进行d次排序    {        for(j = 0; j < 10; j++)            count[j] = 0; //每次分配前清空计数器        for(j = 0; j < n; j++)        {            k = (data[j] / radix) % 10; //统计每个桶中的记录数            count[k]++;        }        for(j = 1; j < 10; j++)            count[j] = count[j - 1] + count[j]; //将tmp中的位置依次分配给每个桶        for(j = n - 1; j >= 0; j--) //将所有桶中记录依次收集到tmp中        {            k = (data[j] / radix) % 10;            tmp[count[k] - 1] = data[j];            count[k]--;        }        for(j = 0; j < n; j++) //将临时数组的内容复制到data中            data[j] = tmp[j];        radix = radix * 10;    }}int main(){    int array[10] = {73,22,93,43,55,14,28,65,39,81};    radixsort(array,10);    printArray(array,10);    return 0;}  

算法比较



当n较大,则应采用时间复杂度为O(nlog2n)的排序方法:快速排序、堆排序或归并排序序。
快速排序:是目前基于比较的内部排序中被认为是最好的方法,当待排序的关键字是随机分布时,快速排序的平均时间最短



原创粉丝点击