冒泡、插入、选择排序及其改进

这三种排序方式可以说是最简单,最常用的三种排序方式了。

值得说的是,在每种排序之后我都附加了其改进版本。

冒泡排序

普通冒泡排序

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] );
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值