冒泡排序的三种优化

Find out why have I failed!


/*
 * 思考:什么叫优化,冒泡排序主要消耗时间的地方在哪里,
 * 主要是在比较和交换元素,耗时操作。
 * 只要减少比较和交换元素的次数就可以起到优化的作用
 */


#include <stdio.h>
#include <stdlib.h>
#include <time.h>


/* 普通的冒泡排序 */
void bubble_sort(int *a, int length){
    for(int i=0;i<length-1;i++){
        for(int j=0;j<length-i-1;j++){
            if(a[j]>a[j+1]){
                int temp = a[j];
                a[j] = a[j+1];
                a[j+1] = temp;
            }
        }
    }
}

/*
 * 优化1:
 * 比如说一个数组,基本有序
 * 初始:1,2,3,4,5,6,7,8,10,9
 * 第一次排序:1,2,3,4,5,6,7,8,9,10                  //比较9次,这时候数组已经有序了,
 * 第二次排序:1,2,3,4,5,6,7,8,9,10                  //比较8次,剩下的比较是无意义的,因为数组已经有序了
 * 那么,我们是不是可以判断,当某一次循环比较结束,
 * 比如说第二次排序没有交换元素的时候,说明数组已经有序了。
 * 所以思路很简单:设标志位判断,如果出现某一次循环没有交换元素,则基本有序
 *
*/


void bubble_sort_level1(int *a,int length){
    for(int i=0;i<length-1;i++){
        int flag = false;                         //每次进行一次循环的时候,
        for(int j=0;j<length-i-1;j++){
            if(a[j]>a[j+1]){
                int temp = a[j];
                a[j] = a[j+1];
                a[j+1] = temp;
                flag = true;                      //只要flag == true
            }
        }
        if(flag == false){
            break;
        }
    }
}




/*
 * 优化2:
 * 比如说一个数组,基本有序,因为是举例,都比较简单,
 * 但是原理都是一样的,重要的是要学会自己思考,自己理解
 * 初始:1,5,3,2,4,6,7,8,9,10
 * 第一次排序:1,3,2,4,5,6,7,8,9,10               //记录最后一次交换的位置,(从0开始,下标为4,总的length长度10,)
 * 第二次排序:1,2,3,4,5,6,7,8,9,10               //发现第二次排序就变成有序了,
 *
 *
 * 这个时候,我们可以确定了,最后一次交换的位置是4,说明什么。
 * 说明4之后的元素都是有序的了,冒泡排序,每次都会把最大的往最后推,
 * 设置最后一个交换的位置为x,如果x < length-1,说明
 * x到length-1,也就是到最后的位置基本有序。
 * 所以在每次排序的时候记录最大位置下标,每次进行比较的时候,
 * 后面的就不用再比了。
 *
*/


void bubble_sort_level2(int *a,int length){
    int lastpost = 0;
    int k;
    for(int i=0;i<length-1;i++){
        k = length-i-1;
        for(int j=0;j<k;j++){                      //下一次比较到lastpos就可以
            if(a[j]>a[j+1]){
                int temp = a[j];
                a[j] = a[j+1];
                a[j+1] = temp;
                lastpost = j;                      //针对上面这个情况,最后一次为4
            }
        }
        k = lastpost;                              //这个时候比较到lastpos就可以
    }
}



/*
 * 优化3:(每次最大值,最小值,双向冒泡,也叫做鸡尾酒优化)
 * 例如出现如下数组:1,2,3,4,5,6,7,0,10,9
 * 使用普通冒泡,
 * 第一次排序:1,2,3,4,5,6,0,7,9,10           //比较9次,交换两次,7和0交换,9和10交换
 * 第二次排序:1,2,3,4,5,0,6,7,9,10           //比较8次,求出最大值不用再比,0和6交换
 *
 * 这个时候,我们发现了,因为只差一个最小的0元素,
 * 但是后续又要进行length-i-1的比较和交换,只
 * 是为了将最小值0排序到最左边,浪费了时间,所以,
 * 我们可以考虑,是否可以交替进行,双向冒泡呢,即
 * 第一次从最大值往前,然后再从前往后,是可以的。
 *
 * 使用优化的:
 * 第一次排序:1,2,3,4,5,6,0,7,9,10
 * 第二次排序:0,1,2,3,4,5,6,7,9,10
 *
 *
 */


void bubble_sort_level3(int *a, int length){

    /* 切换双向排序 */
    int flag = true;
    int forward = 0;         /* 记录正向 */
    int reverse = 0;         /* 记录反向 */

    for(int i=0;i<length-1;i++){
        if(flag){
            for(int j=reverse;j<length-forward-1;j++){
                if(a[j]>a[j+1]){
                    int temp = a[j];
                    a[j] = a[j+1];
                    a[j+1] = temp;
                }
            }

            forward++;
            flag = false;
        }
        else{
            for(int j=length-forward-1;j>reverse;j--){
                if(a[j-1]>a[j]){
                    int temp = a[j];
                    a[j] = a[j-1];
                    a[j-1] = temp;

                }
            }

            reverse++;
            flag = true;
        }
    }
}


/*
 *
 * 基本上,学习到现在,三种冒泡的优化方法以及原理都了解了,
 * 最终我们可以将三种优化方法进行结合,结合写出最终的冒泡
 *
 * 1.是否交换元素,如果没有,提前结束,减少无意义的次数。
 * 2.记录最后一次交换的下标,如果小于长度则进行替换,因为之后的比较是无意义的。
 * 3.双向冒泡,防止为了只冒泡最大值,或者只冒泡最小值。
 *
 */

void bubble_final_level(int *a, int length){

    /* 切换双向排序 */
    int flag = true;
    int forward = 0;                                                /* 记录正向 */
    int reverse = 0;                                                /* 记录反向 */


    int forward_last_index = length;                                /* 记录正向,最后一次 */
    int reverse_last_index = 0;                                     /* 记录反向,最后一次 */


    for(int i=0;i<length-1;i++){

        /* 正向的正常长度 */
        int forward_length = length-forward-1;
        int reverse_length = reverse;

        int IsExchange = false;

        if(flag){
            for(int j=reverse_length;j<forward_length;j++){
                if(a[j]>a[j+1]){
                    int temp = a[j];
                    a[j] = a[j+1];
                    a[j+1] = temp;
                    IsExchange = true;

                    forward_last_index = j;                              /* 最后一次长度,如果小于forward_length */

                }
            }

            if(IsExchange==false)                                         /* 未交换元素,已经有序,结束 */
                break;

            if(forward_last_index<forward_length){                         /* 则比较到forward_last_index就结束 */
                forward_length = forward_last_index;
            }

            forward++;
            flag = false;
        }
        else{
            for(int j=forward_length;j>reverse_length;j--){
                if(a[j]<a[j-1]){
                    int temp = a[j];
                    a[j] = a[j-1];
                    a[j-1] = temp;
                    IsExchange = true;
                    reverse_last_index = j;                              /* 最后一次长度,如果大于reverse_length */
                }
            }

            if(IsExchange==false)                                        /* 未交换元素,已经有序,结束 */
                break;


            if(reverse_last_index>reverse_length){
                reverse_length = reverse_last_index;                      /* 则从reverse_last_index开始算起 */
            }

            reverse++;
            flag = true;
        }
    }

}





/* 生成测试数据,写一个简单随机数生成的 */
void ProductTestData(int *p, int length){

    srand((unsigned)time(NULL));
    for(int i=0;i<length;i++){
        p[i] = rand()%101;
    }

}


void ArrayPrint(int *p,int length){

    printf("show:\n");

    for(int i=0;i<length;i++){
        printf("%3d",p[i]);
    }
    printf("\n");
}


int main(){

//   生成测试数据
//    int a[10000] = {0};
//    ProductTestData(a,10000);
//    for(int i=0;i<30;i++){
//        printf("%d,",a[i]);
//    }

    /* 三组数据测试优化 */
    int Test1[30] = {52,84,50,49,78,29,17,53,25,3,38,5,11,8,44,87,80,84,61,10,47,16,51,9,97,60,88,66,57,45};
    int Test2[30] = {52,84,50,49,78,29,17,53,25,3,38,5,11,8,44,87,80,84,61,10,47,16,51,9,97,60,88,66,57,45};
    int Test3[30] = {52,84,50,49,78,29,17,53,25,3,38,5,11,8,44,87,80,84,61,10,47,16,51,9,97,60,88,66,57,45};
    int Test4[30] = {52,84,50,49,78,29,17,53,25,3,38,5,11,8,44,87,80,84,61,10,47,16,51,9,97,60,88,66,57,45};
    int Test5[30] = {52,84,50,49,78,29,17,53,25,3,38,5,11,8,44,87,80,84,61,10,47,16,51,9,97,60,88,66,57,45};



    bubble_sort(Test1,30);
    bubble_sort_level1(Test2,30);
    bubble_sort_level2(Test3,30);
    bubble_sort_level3(Test4,30);
    bubble_final_level(Test5,30);


    ArrayPrint(Test1,30);
    ArrayPrint(Test2,30);
    ArrayPrint(Test3,30);
    ArrayPrint(Test4,30);

    ArrayPrint(Test5,30);



    system("pause");
    return 0;
}

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值