//稳定排序:大小相同的元素排序后它们顺序不变。
//
//排序方法选择,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)
//
//堆排序适合于数据量非常大的场合(百万数据)。
//
//堆排序不需要大量的递归或者多维的暂存数组。
//这对于数据量非常巨大的序列是合适的。
//比如超过数百万条记录,因为快速排序,归并排序都使用递归来设计算法
//在数据量非常大的时候,可能会发生堆栈溢出错误。
//
//堆排序会将所有的数据建成一个堆,最大的数据在堆顶
//然后将堆顶数据和序列的最后一个数据交换
//接下来再次重建堆,交换数据,依次下去,就可以排序所有的数据。
排序算法
最新推荐文章于 2025-08-06 17:01:48 发布