冒泡排序的三种实现

本文详细介绍了冒泡排序的基本原理及多种优化方式,包括带标志的普通冒泡排序、双向冒泡排序、奇偶冒泡排序等,并对比了它们的性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  冒泡排序是非常容易理解和实现,,以从小到大排序举例:

设数组长度为N。

1.比较相邻的前后二个数据,如果前面数据大于后面的数据,就将二个数据交换。

2.这样对数组的第0个数据到N-1个数据进行一次遍历后,最大的一个数据就“沉”到数组第N-1个位置。

3.N=N-1,如果N不为0就重复前面二步,否则排序完成。

 

按照定义很容易写出代码:

[cpp]  view plain copy
  1. //冒泡排序1  
  2. void BubbleSort1(int a[], int n)  
  3. {  
  4.        int i, j;  
  5.        for (i = 0; i < n; i++)  
  6.               for (j = 1; j < n - i; j++)  
  7.                      if (a[j - 1] > a[j])  
  8.                             Swap(a[j - 1], a[j]);  
  9. }  


下面对其进行优化,设置一个标志,如果这一趟发生了交换,则为true,否则为false。明显如果有一趟没有发生交换,说明排序已经完成。

[cpp]  view plain copy
  1.   
[cpp]  view plain copy
  1. //冒泡排序2  
  2. void BubbleSort2(int a[], int n)  
  3. {  
  4.        int j, k;  
  5.        bool flag;  
  6.   
  7.        k = n;  
  8.        flag = true;  
  9.        while (flag)  
  10.        {  
  11.               flag = false;  
  12.               for (j = 1; j < k; j++)  
  13.                      if (a[j - 1] > a[j])  
  14.                      {  
  15.                             Swap(a[j - 1], a[j]);  
  16.                             flag = true;  
  17.                      }  
  18.               k--;  
  19.        }  
  20. }  

再做进一步的优化。如果有100个数的数组,仅前面10个无序,后面90个都已排好序且都大于前面10个数字,那么在第一趟遍历后,最后发生交换的位置必定小于10,且这个位置之后的数据必定已经有序了,记录下这位置,第二次只要从数组头部遍历到这个位置就可以了。

[cpp]  view plain copy
  1. //冒泡排序3  
  2. void BubbleSort3(int a[], int n)  
  3. {  
  4.     int j, k;  
  5.     int flag;  
  6.       
  7.     flag = n;  
  8.     while (flag > 0)  
  9.     {  
  10.         k = flag;  
  11.         flag = 0;  
  12.         for (j = 1; j < k; j++)  
  13.             if (a[j - 1] > a[j])  
  14.             {  
  15.                 Swap(a[j - 1], a[j]);  
  16.                 flag = j;  
  17.             }  
  18.     }  
  19. }  

冒泡排序毕竟是一种效率低下的排序方法,在数据规模很小时,可以采用。数据规模比较大时,最好用其它排序方法。



今天把几种冒泡排序都实现了一下,大概总结一下,个人笔记,有误帮我指出,谢谢!

我总结的大概有这几种冒泡排序:

1、            带标志的普通冒泡排序

2、            双向冒泡排序(鸡尾酒冒泡排序)

3、            奇偶冒泡排序

4、            局部冒泡排序

5、            快速排序(不稳定)

先来看代码:

1、

//简单冒泡排序

void SimSort( int a[], int n)

{

       int i, j, tmp, tag = 1;  //tag为1表示发生了交换

       for( i=1; i<n && tag; i++ )       //没有元素交换则停止循环         

       { 

              tag = 0;  //0表示没有交换

              for( j=0; j<n-i; j++ )

              {

                     if( a[j] > a[j+1] )

                     {

                            tmp = a[j];

                            a[j] = a[j+1];

                            a[j+1] = tmp;

                            tag = 1;

                     }

              }

       }

}

带标志的最简单,也非常有效,全部排序都需要这个,新手这个务必掌握。只要这一趟没有发生交换,那么就会停止循环扫描,减少了扫描的次数。一个很简单的例子,比如一个顺序的一组元素,就拿1,2,3,4,5来说吧,第一趟扫描完,发现没有元素交换,如果没有标志,最外层循环条件为for( i=1; i<n; i++ ),还会继续扫描,虽然一直没有元素交换,浪费了时间。

2、

//双向冒泡排序

void dbSort( int a[], int n )

{

       int i, j, k, tmp, tag=1; //tag=1为发生了交换的标志

       for( i=1; tag ; i++ )//如果tag为1则发生了交换,继续循环,为0则停止

       {

              tag = 0;

              for( j=n-i; j>=i; j-- )//由后往前找最小的元素

              {

                     if( a[j] < a[j-1] )                       

                    {

                            tmp = a[j];

                            a[j] = a[j-1];

                            a[j-1] = tmp;

                            tag = 1;

                     }

              }

              for( k=i; k<n-i; k++ )//由前往后找最大的元素

              {

                     if( a[k] > a[k+1] )

                     {

                            tmp = a[k];

                            a[k] = a[k+1];

                            a[k+1] = tmp;

                            tag = 1;

                     }

              }

       }

}

双向冒泡排序每一趟都是先从后往前扫找最大的元素,然后从前往后找最小的元素。对随机序列排序性能稍高于普通冒泡排序,但是因为是双向冒泡,每次循环都双向检查,极端环境下会出现额外的比较,导致算法性能的退化,比如“4、5、7、1、2、3”这个序列就会出现退化。

3、

//奇偶冒泡排序

void OddEvenSort( int a[], int n )

{

       int i, tmp, tag=1; //tag=1为发生了交换的标志

       do

       {

              tag = 0;  //设奇数趟无交换

              for( i=1; i<n-1; i+=2 ) //扫描所有奇数项

              {

                     if( a[i] > a[i+1] )

                     {

                            tmp = a[i];

                            a[i] = a[i+1];

                            a[i+1] = tmp;

                            tag = 1;

                     }

              }

              tag = 0;  //设偶数趟无交换

              for( i=0; i<n-1; i+=2 ) //扫描所有偶数项

              {

                     if( a[i] > a[i+1] )

                     {

                            tmp = a[i];

                            a[i] = a[i+1];

                            a[i+1] = tmp;

                            tag = 1;

                     }

              }

       }

       while( tag );

}

奇偶冒泡排序是每一趟都是扫描奇数项,交换相邻的两项元素,然后扫描偶数项,交换相邻的元素。比如一组元素:5,3,2,1,9,4,先扫描奇数项的,假如都要交换,先交换5和3,再交换2和1,最后交换9和4;然后扫描偶数项,交换3和2,交换1和9(这个实际不用交换,为举例说明而已),这是第一趟,每一趟都是如此,直到没有交换了为止,即tag=0。每一趟都要扫描那么多的元素,并不像上面的越到后面的趟元素越少,看起来貌似会花更多的时间,这个有兴趣的可以测试一下,和一般的冒泡比较一下。

4、局部冒泡排序

这个只是在网上看到了有这样的冒泡排序,还有待研究,会得教我一下。

在冒泡排序中,一趟扫描有可能无数据交换,也有可能有一次或多次数据交换,在传统的冒泡排序算法及近年来的一些改进的算法中,只记录一趟扫描有无数据交换的信息,对数据交换发生的位置信息则不予处理。为了充分利用这一信息,可以在一趟全局扫描中,对每一反序数据对进行局部冒泡排序处理,称之为局部冒泡排序。

局部冒泡排序与冒泡排序算法具有相同的时间复杂度,并且在正需和逆序的情况下,所需的关键字的比较次数和移动次数完全相同。由于局部冒泡排序和冒泡排序的数据移动次数总是相同的,而局部冒泡排序所需关键字的比较次数常少于冒泡排序,这意味着局部冒泡排序很可能在平均比较次数上对冒泡派粗有所改进,当比较次数较少的优点不足以抵消其程序复杂度所带来的额外开销,而当数据量较大时,局部冒泡排序的时间性能则明显优于冒泡排序。

5、快速排序

快速排序不稳定,但是是最快的内部排序(书上说的)。快速排序也有好几种变种,这个得另外总结。

以上是我个人的几点总结,不能说是总结吧,因为没总结什么出来,只是给自己做个笔记,以后需要容易找得到。

 

大四找实习中……没啥要求,哪里都可以,首选广东,因为我是广东清远的,现在在赣州读书。望有门路的朋友告诉我,无限感激!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值