冒泡排序算法及其改进(C语言实现)

一、经典冒泡算法

假设有n个数需要排列成非递减数列。冒泡算法的主要思想是
①比较第一个与第二个数,如果第二个数小于第一个数则交换,否则不交换;然后以同样的规则比较第二第三个数,以此类推,比如
在这里插入图片描述

这样就完成了一趟排序,通过不断地交换,第一趟排序进行了n-1次比较就把最大的数交换到了最后。
②遵循①中的规则进行下一趟排序,由于最后一位已经确定是最大值,则第二次只需要n-2次比较既可以把第二大的数交换到倒数第二个位置。
③以此类推,第i趟排序需要经过n-i次比较
其算法具体实现如下:

#include <stdio.h>

void sort(int* N,int n){
    int i,j,k;
    // 外循环控制趟数,一共需要n-1趟
    for(i = n - 1; i > 0; i--){
        // 内循环控制每一趟比较的次数
        for(j = 0; j < i; j++){
            // 相邻两个数进行比较,如果前一个数较大则进行交换
            if(N[j] > N[j + 1]){
                k = N[j];
                N[j] = N[j + 1];
                N[j + 1] = k;
            }
        }
    }
}
int main(int argc, char **argv)
{
    int N[6] = {25,14,54,63,44,3};
    int n = 6;
    sort(N,n);
    int i;
    for(i = 0; i < n; i++){
        printf("%d",N[i]);
        if(i != n - 1) printf(",");
    }
    return 0;
}

运行结果:
在这里插入图片描述

二、冒泡算法的改进

冒泡算法的算法时间复杂度显然为o(n2),效率十分低下。在有些情况下,算法执行若干次后,可能已经是有序序列了,但是上面的冒泡算法已经执行后面的比较,知道执行完n-1趟排序,这样显然不是最佳的方式。这时候我们可以设置一个标记,用来判断一趟排序过后有没有发生交换,如果没有发生交换,则说明数组已经有序了,已经不需要再进行余下的比较

#include <stdio.h>

void sort(int* N,int n){
    int i,j,k,l;
    // 外循环控制趟数,一共需要n-1趟
    for(i = n - 1; i > 0; i--){
        // l初始化为0,用于记录每一趟排序的交换次数,
        //如果一趟排序过后交换次数依然是0,则表示数组已经是有序的了
        l = 0;
        // 内循环控制每一趟比较的次数
        for(j = 0; j < i; j++){
            // 相邻两个数进行比较,如果前一个数较大则进行交换
            if(N[j] > N[j + 1]){
                // 如果发生了交换,l自增
                l++;
                k = N[j];
                N[j] = N[j + 1];
                N[j + 1] = k;
            }
            
        }
        if(l != 0) printf("第%d趟排序,发生了%d次交换\n",n - i,l);
        else {
            printf("第%d趟排序,没有发生交换,数组已经有序\n",n - i);
            break;
        }
    }
}
int main(int argc, char **argv)
{
    int N[6] = {1,2,3,4,5,6};
    int n = 6;
    sort(N,n);
    int i;
    for(i = 0; i < n; i++){
        printf("%d",N[i]);
        if(i != n - 1) printf(",");
    }
    return 0;
}

运行结果:
在这里插入图片描述

这是一个比较极端的情况,需要排序的数组本来就是有序的。可以看出程序只执行了一趟排序,而不是传统冒泡排序的n-1趟排序,效率有所提高。

三、冒泡算法的再改进

在某些情况下,如果进行了若干次排序之后,后面的若干个数已经是有序的,那么下一趟排序就只需要比较前面无序的那一段就可以了。所以我们可以设置一个标记用来记录每趟排序最后一个发生交换的位置,下一趟排序值比较到此位置即可。

#include <stdio.h>


void sort(int* N,int n){
    int i,j,k,flag;
    // 初始化标记为-1
    flag = n - 1;
    while(flag > 0){
        i = flag;
        flag = 0;
        for(j = 0; j < i; j++){
            if(N[j] > N[j + 1]){
                k = N[j];
                N[j] = N[j + 1];
                N[j + 1] = k;
                flag = j;
            }
        }
        printf("最后一次发生交换的位置为%d\n",flag);
    }


}
int main(int argc, char **argv)
{
    int N[6] = {2,1,4,3,5,6};
    int n = 6;
    sort(N,n);
    int i;
    for(i = 0; i < n; i++){
        printf("%d",N[i]);
        if(i != n - 1) printf(",");
    }
    return 0;
}

运行结果:
在这里插入图片描述

虽然以上两种算法对传统的冒泡排序做了一定程度的优化,但是算法复杂度还是o(n2),所以最好的优化方法就是——能不用冒泡算法就不用冒泡算法,改用算法复杂度小的。

  • 11
    点赞
  • 70
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值