选择类排序
简单选择排序
空间复杂度:需要额外的存储空间仅为交换元素时借助的中间变量,所以空间复杂度是O(1) 时间复杂度: 关键操作在于交换元素操作,整个算法由双重循环组成,外层循环从0到n-2一共n-2+1=n-1次, 对于第i层外层循环,内层循环执行n-1-(i+1)+1=n-i-1次。 当i=0,内层循环执行n-1次,当i=n-2,内层循环执行1次,所以是一个等差数列求和,一共为(1+n-1)(n-1)/2=n(n-1)/2 ,所以时间复杂度为O(n^2) 稳定性:不稳定 原因就在于交换部分会打破相对顺序
void Select_Sort ( int * list, int count)
{
int min, i, j;
for ( i= 0 ; i< count; i++ )
{
min = i;
for ( j= i+ 1 ; j< count; j++ )
{
if ( list[ min] > list[ j] )
{
min = j;
}
}
if ( min!= i)
{
swap ( list[ i] , list[ min] ) ;
}
}
}
堆排序
什么是堆?
堆是一棵完全二叉树,而且满足任何一个非叶结点的值都不大于(或不小于)其左右孩子结点的值。
如果是每个结点的值都不小于它的左右孩子结点的值,则称为大顶堆。 如果是每个结点的值都不大于它的左右孩子结点的值,则称为小顶堆。 什么是堆排序?
我们知道对于一个堆来说,它的根结点是整个堆中所有结点的值的最大值(大顶堆)或者最小值(小顶堆)。所以堆排序的思想就是每次将无序序列调节成一个堆,然后从堆中选择堆顶元素的值,这个值加入有序序列,无序序列减少一个,再反复调节无序序列,直到所有关键字都加入到有序序列。 时间复杂度: 堆排序的总时间可以分为①建堆部分+②n-1次向下调整堆 堆排序的时间复杂度为O(n)+O(nlog2n)=O(nlog2n) 堆排序不稳定
void Heap_Adjust ( int * list, int s, int m)
{
int temp = list[ s] ;
for ( int j= 2 * s+ 1 ; j<= m; j = 2 * j+ 1 )
{
if ( list[ j] < list[ j+ 1 ] && j< m)
{
j++ ;
}
if ( temp> list[ j] )
break ;
list[ s] = list[ j] ;
s = j;
}
list[ s] = temp;
}
void Heap_Sort ( int * list, int len)
{
for ( int s = len/ 2 - 1 ; s>= 0 ; s-- )
{
Heap_Adjust ( list, s, len- 1 ) ;
}
for ( int i = len- 1 ; i >= 1 ; i-- )
{
swap ( list[ 0 ] , list[ i] ) ;
Heap_Adjust ( list, 0 , i- 1 ) ;
}
}
归并排序
假定待排序表含有n个记录,则可以看成是n个有序的子表,每个子表长度为1,然后两两归并,得到 ⌈n/2⌉个长度为2或1的有序表;再两两归并,……如此重复,直到合并成一个长度为n的有序表为止,这种排序方法称为2-路归并排序。 例如:49 38 65 97 76 13 27
①首先将整个序列的每个关键字看成一个单独的有序的子序列 ②两两归并,49和38归并成{38 49} ,65和97归并成{65 97},76和13归并成{13 76},27没有归并对象 ③两两归并,{38 49}和{65 97}归并成{38 49 65 97},{13,76}和27归并成{13 27 76} ④两两归并,{38 49 65 97}和{13 27 76}归并成{13 27 38 49 65 76 97} 时间复杂度:O(nlog2n) 空间复杂度:因为需要将这个待排序序列转存到一个数组,所以需要额外开辟大小为n的存储空间,即空间复杂度为O(n) 稳定性:稳定
void Merge ( int * list, int start, int mid, int end)
{
const int len1 = mid - start + 1 ;
const int len2 = end - mid;
const int len = end - start + 1 ;
int i, j, k;
int * front = ( int * ) malloc ( sizeof ( int ) * len1) ;
int * back = ( int * ) malloc ( sizeof ( int ) * len2) ;
for ( i= 0 ; i< len1; i++ )
front[ i] = list[ start+ i] ;
for ( j= 0 ; j< len2; j++ )
back[ j] = list[ mid+ j+ 1 ] ;
for ( i= 0 , j= 0 , k= start; i< len1&& j< len2&& k< end; k++ )
{
if ( front[ i] < back[ j] )
{
list[ k] = front[ i] ;
i++ ;
} else
{
list[ k] = back[ j] ;
j++ ;
}
}
while ( i< len1)
{
list[ k++ ] = front[ i++ ] ;
}
while ( j< len2)
{
list[ k++ ] = back[ j++ ] ;
}
}
void MSort ( int * list, int start, int end)
{
if ( start< end)
{
int mid = ( start+ end) / 2 ;
MSort ( list, 0 , mid) ;
MSort ( list, mid+ 1 , end) ;
Merge ( list, start, mid, end) ;
}
}
void Merge_Sort ( int * list, int count)
{
MSort ( list, 0 , count- 1 ) ;
}
基数排序
基数排序(也叫桶排序)是一种很特别的排序方法,它不是基于比较进行排序的,而是采用多关键字排序思想(即基于关键字各位的大小进行排序的),借助“分配”和“收集”两种操作对单逻辑关键字进行排序。基数排序又分为最高位优先(MSD)排序和最低位优先(LSD)排序。 例子:53, 3, 542, 748, 14, 214, 154, 63, 616
补充位数:053, 003, 542, 748, 014, 214, 154, 063, 616 桶实际是一个队列,先进先出(从桶的上面进,下面出) 关键字数量为n,关键字的位数为d,比如748 d=3,r为关键字的基的个数,就是组成关键字的数据的种类,比如十进制数字一共有0至9一共10个数字,即r=10 空间复杂度:需要开辟关键字基的个数个队列,所以空间复杂度为O® 时间复杂度:需要进行关键字位数d次"分配"和"收集",一次"分配"需要将n个关键字放进各个队列中,一次"收集"需要将r个桶都收集一遍。所以一次"分配"和一次"收集"时间复杂度为O(n+r)。d次就需要O(d(n+r))的时间复杂度。 稳定性:由于是队列,先进先出的性质,所以在分配的时候是按照先后顺序分配,也就是稳定的,所以收集的时候也是保持稳定的。即基数排序是稳定的排序算法。
* #define MAX 20
#define BASE 10
void Radix_Sort ( int * a, int n) {
int i, b[ MAX] , m = a[ 0 ] , exp = 1 ;
for ( i = 1 ; i < n; i++ ) {
if ( a[ i] > m) {
m = a[ i] ;
}
}
while ( m / exp > 0 ) {
int bucket[ BASE] = { 0 } ;
for ( i = 0 ; i < n; i++ ) {
bucket[ ( a[ i] / exp) % BASE] ++ ;
}
for ( i = 1 ; i < BASE; i++ ) {
bucket[ i] + = bucket[ i - 1 ] ;
}
for ( i = n - 1 ; i >= 0 ; i-- ) {
b[ -- bucket[ ( a[ i] / exp) % BASE] ] = a[ i] ;
}
for ( i = 0 ; i < n; i++ ) {
a[ i] = b[ i] ;
}
exp * = BASE;
}
}