排序算法

来源:互联网 发布:michael angelo知乎 编辑:程序博客网 时间:2024/06/03 17:53
 
//稳定排序:大小相同的元素排序后它们顺序不变。////排序方法选择,n表示数据长度:/*  1、若n较小(n <= 50)可以用插入排序、选择排序,如果再小的话就考虑使用插入排序;  2、若文件初始是大致有序的,应考虑插入排序、冒泡排序;  3、如果n很大5000+,则考虑快速排序、堆排序、归并排序;*/ //排序法          平均时间          最差情形         稳定度       额外空间             备注 //冒泡              O(n2)          O(n2)         稳定           O(1)              n小时较好,大部分已排序时较好  //交换              O(n2)          O(n2)         不稳定         O(1)              n小时较好 //选择              O(n2)          O(n2)         不稳定         O(1)              n小时较好 //插入              O(n2)          O(n2)         稳定           O(1)              大部分已排序时较好 //基数              O(logRB)     O(logRB)        稳定           O(n)              B是真数(0-9),R是基数(个十百)////Shell          O(nlogn)      O(ns)      1<s<2 不稳定       O(1)              s是所选分组 //快速              O(nlogn)      O(n2)          不稳定         O(nlogn)          n大时较好 //归并              O(nlogn)      O(nlogn)       稳定           O(1)              n大时较好 //堆                O(nlogn)      O(nlogn)       不稳定         O(1)              n大时较好   //****************************************************************************************************//插入排序//从第一个开始,记录data[i]为temp//将i左边所有比temp大的元素data[j-1]都向后移动一位//然后将temp插入到空出来的位置template<class T>void insertSort( T data[], const size_t &n ){ for ( size_t i=0,j=0; i<n; i++ ) {  T temp = data[i];  for ( j=i; j>0 && temp < data[j-1]; j--)  {   data[j] = data[j-1];  }  data[j] = temp; }} //****************************************************************************************************//选择排序//起始从第一个元素开始i//找到i到n中最小的放在i位置//i++template<class T>void selectSort( T data[], const size_t &n ){ for ( size_t i=0,j,least; i<n-1; i++ ) {  for ( j=i+1,least=i; j<n; j++ )  {   if ( data[j] < data[least] )   {    least = j;   }  }  swap( data[least], data[i] ); }}  //****************************************************************************************************//冒泡排序//想像成一个柱体,最小的在低端,会像起泡一样冒上来//每次i循环结束都冒上来剩余元素中最小的//记录每次i循环标志是否发生交换,如果没发生则排序结束,终止循环template<class T>void bubbleSort( T data[], const size_t &n ){ bool isExchange = false; for ( size_t i=0,j; i<n-1; i++ ) {  isExchange = false;  for ( j=n-1; j>i; j--)  {   if ( data[j] < data[j-1] )   {    swap( data[j], data[j-1] );    isExchange = true;   }  }  if ( !isExchange )  {   return;  } }}  //****************************************************************************************************//Shell排序////Shell排序通过将数据分成不同的组,先对每一组进行排序//然后再对所有的元素进行一次插入排序,以减少数据交换和移动的次数。//演示如下链接//http://student.zjzk.cn/course_ware/data_structure/web/flashhtml/shell.htm//其中分组的合理性会对算法产生重要的影响。现在多用D.E.Knuth的分组方法。////Shell排序比冒泡排序快5倍,比插入排序大致快2倍。//Shell排序比起QuickSort,MergeSort,HeapSort慢很多。//但是它相对比较简单,它适合于数据量在5000以下并且速度并不是特别重要的场合。//它对于数据量较小的数列重复排序是非常好的。template<class T>void shellFun( T data[], const size_t &n, const size_t &d){ for ( size_t i = d+1; i<n; i++ ) {  T temp = data[i];  size_t j = i;  while ( j-d>=0 && temp < data[j-d] )  {   data[j] = data[j-d];    j -= d;  }  data[j] = temp; }}template<class T>void shellSort( T data[], const size_t &n ){ size_t d = n; while ( d>1 ) {  d = d/3 + 1;  shellFun( data, n, d ); }}    //****************************************************************************************************//快速排序(QuickSort)////快速排序是一个就地排序,分而治之,大规模递归的算法。//从本质上来说,它是归并排序的就地版本。快速排序可以由下面四步组成。////(1) 如果不多于1个数据,直接返回。//(2) 一般选择序列最左边的值作为支点数据。//(3) 将序列分成2部分,一部分都大于支点数据,另外一部分都小于支点数据。//(4) 对两边利用递归排序数列。////快速排序比大部分排序算法都要快。尽管我们可以在某些特殊的情况下写出比快速排序快的算法,//但是就通常情况而言,没有比它更快的了。//快速排序是递归的,对于内存非常有限的机器来说,它不是一个好的选择。 template<class T>void quickFun( T data[], const size_t &first, const size_t &last ){ size_t lower = first+1; size_t upper = last; funtemp( data ); swap( data[first], data[(first+last) >> 1] ); funtemp( data ); T bound = data[first]; while( lower <= upper ) {  while ( lower <= upper && data[lower] < bound )  {   ++lower;  }  while ( lower <= upper && bound < data[upper] )  {   --upper;  }  if ( lower < upper )  {   swap( data[lower++], data[upper--] );   funtemp( data );  }  else  {   ++lower;  } } swap( data[upper], data[first] ); funtemp( data ); if ( 0 < upper && first < upper-1 ) //这里用的size_t不判断upper大于0就悲剧了 {  quickFun( data, first, upper-1 ); } if ( upper+1 < last ) {  quickFun( data, upper+1, last );; }}template<class T>void quickSort( T data[], const size_t &n ){ if ( n < 2 ) {  return; } size_t max = 0; for ( size_t i=1; i<n; i++ ) {  if ( data[max] < data[i] )  {   max = i;  } } swap( data[n-1],data[max]); quickFun( data, 0, n-2 );}    //****************************************************************************************************//归并排序//归并排序先分解要排序的序列,从1分成2,2分成4,依次分解,//当分解到只有1个一组的时候,就可以排序这些分组,//然后依次合并回原来的序列中,这样就可以排序所有数据。//归并排序比堆排序稍微快一点,但是需要比堆排序多一倍的内存空间,因为它需要一个额外的数组。template<class T>void MergeFun( T L1[], T L2[], const size_t &first, const size_t &last ){ for(size_t i=first; i<=mid; i++)  L2[i] = L1[i]; size_t k = last; for(size_t j=mid+1; j<=last; j++)  L2[j] = L1[k--]; size_t s1 = first; size_t s2 = last; size_t t = first; while(t<=last) {  if(L2[s1] <= L2[s2])   L1[t++] = L2[s1++];  else   L1[t++] = L2[s2--]; }}template<class T>void MergeSort( T L1[], T L2[], const size_t &first, const size_t &last ){ if(first >= last) {  return; } size_t mid = (first+last)/2; MergeSort(L1, L2, first, mid); MergeSort(L1, L2, mid+1, last); MergeFun(L1, L2, first, mid, last);}    //****************************************************************************************************//基数排序(RadixSort)////基数排序和通常的排序算法并不走同样的路线。//它是一种比较新颖的算法,但是它只能用于整数的排序,//如果我们要把同样的办法运用到浮点数上,我们必须了解浮点数的存储格式,//并通过特殊的方式将浮点数映射到整数上,然后再映射回去,这是非常麻烦的事情,//因此,它的使用同样也不多。//而且,最重要的是,这样算法也需要较多的存储空间。   //****************************************************************************************************//堆排序(HeapSort)////堆排序适合于数据量非常大的场合(百万数据)。////堆排序不需要大量的递归或者多维的暂存数组。//这对于数据量非常巨大的序列是合适的。//比如超过数百万条记录,因为快速排序,归并排序都使用递归来设计算法//在数据量非常大的时候,可能会发生堆栈溢出错误。////堆排序会将所有的数据建成一个堆,最大的数据在堆顶//然后将堆顶数据和序列的最后一个数据交换//接下来再次重建堆,交换数据,依次下去,就可以排序所有的数据。