这三种排序方式可以说是最简单,最常用的三种排序方式了。
值得说的是,在每种排序之后我都附加了其改进版本。
冒泡排序
普通冒泡排序
void bubble_sort( int *array, int size )
{
int i, j, temp;
if ( size <= 1 )
return ;
//i控制遍历数组的次数
//如果要排序size个数字,只需要遍历数组size-1即可
//因为每遍历一次数组就会有一个数字被排好序,
//最后一个数字在前面的数字都排好序之后会自然处于正确的位置
for ( i = 0; i < size-1; ++i )
//j控制每次遍历数组需要遍历的数字个数
for ( j = 0; j < size-1-i; ++j )
{
if ( array[j] > array[j+1] )
{
temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
}
}
}
改进一:
冒泡排序中如果这一趟里面没有交换任何元素,说明数组中每个元素都一排好序。就不用再继续比较了。
void bubble_sort_adv1( int *array, int size )
{
int i, j, temp;
int flag = 1; //增加标记,标记这一趟有没有交换过元素 1 交换过 0 没有交换过
for ( i = 0; i < size-1 && flag; i++ )
{
flag = 0; //每次开始一趟之前设flag为0
for ( j = 0; j < size-i-1; ++j )
{
if ( array[j] > array[j+1] )
{
//出现交换
flag = 1;
temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
}
}
}
}
改进二:
每次记住每趟第一次交换的位置和最后一次交换的位置,第一次交换位置之前的元素都已经排好序了,同理,最后一次交换的位置之后的元素都已经排好序了,这个不难想到,手动走一遍冒泡排序就可以看出来了,知道了这个规律就可以写出下面的代码了。
void bubble_sort_adv2( int *array, int size )
{
int i, j, temp;
//last_swap_end 记录本趟最后一次交换的位置
//last_swap_start 本趟第一次交换的位置
int last_swap_end = size - 1, last_swap_start = 0;
int q = size -1;
int p = last_swap_start;
int first_flag = 1; //记录是否为第一次交换
for ( i = 0; i < size-1; i++ )
{
for ( j = p; j < q; j++ )
{
if ( array[j] > array[j+1] )
{
temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
last_swap_end = j;
if ( first_flag )
{
last_swap_start = j == 0 ? 0 : j -1;
first_flag = 0;
}
}
}
q = last_swap_end;
first_flag = 1;
p = last_swap_start;
}
}
插入排序
//普通插入排序
void insert_sort( int *array, int size )
{
int i, j, temp;
if ( size <= 1 )
return ;
for ( i = 1; i < size; i++ )
{
for ( j = i; j > 0; j-- )
{
if ( array[j] >= array[j-1] )
break;
temp = array[j];
array[j] = array[j-1];
array[j-1] = temp;
}
}
}
改进一:融合二分查找
//哨兵和二分查找相结合的插入排序
void insert_sort_adv2( int *array, int size )
{
int i, j, target;
int k;
for ( i = 1; i < size; ++i )
{
target = array[i];
//找到target应该插入的位置
k = bin_search( array, 0, i, target );
for ( j = i-1; j >= k; --j )
{
array[j+1] = array[j];
}
array[k] = target;
}
}
//[start, end)
//target在array中存在,返回对应下标,不存在返回较大下标
int bin_search( int *array, int start, int end, int target )
{
while ( start < end )
{
int mid = ( start + end ) / 2;
if ( array[mid] == target )
return mid;
else if ( array[mid] < target )
start = mid+1;
else
end = mid;
}
return start;
}
改进二:加入哨兵位
//带哨兵的插入排序
void insert_sort_adv1( int *array, int size )
{
int i, j, temp;
int sentry;
for ( i = 1; i < size; ++i )
{
sentry = array[i];
for ( j = i-1; j >= 0; j-- )
{
if ( sentry < array[j] )
array[j+1] = array[j];
else break;
}
if ( j+1 != i )
array[j+1] = sentry;
}
}
选择排序
普通选择排序
void select_sort( int *array, int size )
{
if ( size <= 1 ) return ;
int i, j, temp;
int mini;
for ( i = 0; i < size-1; ++i )
{
mini = i;
for ( j = i+1; j < size; ++j )
if ( array[mini] > array[j] )
mini = j;
if ( mini == i )
continue;
temp = array[mini];
array[mini] = array[i];
array[i] = temp;
}
}
改进:二元选择排序
普通的选择排序每遍历一次只能找到最小元素,但是其实再一次遍历中同时找到最大元素,这样每次遍历就能确定两个元素的位置。
//二元选择排序
void select_sort_adv1( int *array, int size )
{
int i, j, temp;
int mini; int maxi;
for ( i = 0; i < size/2; i++ )
{
mini = i;
maxi = i;
for ( j = i+1; j < size-i; j++ )
{
if ( array[j] < array[mini] )
mini = j;
else if ( array[j] > array[maxi] )
maxi = j;
}
if ( mini != i )
swap( &array[i], &array[mini] );
if ( maxi != i && maxi != j )
swap( &array[j-1], &array[maxi] );
}
}