内部排序算法:冒泡排序

基本思想


将被排序的记录数组R[0..n-1]垂直排列,每个记录R[i]看作是重量为R[i].key的气泡。根据轻气泡不能在重气泡之下的原则,从下往上扫描数组R:凡扫描到违反本原则的轻气泡,就使其 向上”飘浮”。如此反复进行,直到最后任何两个气泡都是轻者在上,重者在下为止。
具体过程,如下所示:


初始状态:R[0..n-1]为无序区。
第一趟扫描:从无序区底部向上依次比较相邻的两个气泡的重量,若发现轻者在下、重者 在上,则交换二者的位置,即依次比较(R[n-1], R[n-2])、(R[n-2], R[n-3])、…、(R[1], R[0]);对于每对气泡(R[j+1], R[j]),若R[j+1].key第一趟扫描完毕时,”最轻”的气泡就飘浮到该区间的顶部,即关键字最小的记录被放在最高位置R[0]上。
第二趟扫描:扫描R[1..n-1]。扫描完毕时,”次轻”的气泡飘浮到R[1]的位置上……最后,经过n-1趟扫描可得到有序区R[0..n-1]。
注意:
第i趟扫描时,R[0..i-1]和R[i..n-1]分别为当前的有序区和无序区。扫描仍是从无序区底 部向上直至该区顶部。扫描完毕时,该区中最轻气泡飘浮到顶部位置R[i]上,结果是R[0..i]变为新的有序区。


算法实现


冒泡排序算法,Java实现,代码如下所示:


public abstract class Sorter {
     public abstract void sort(int[] array);
}

public class BubbleSorter extends Sorter {

     @Override
     public void sort(int[] array) {
          int tmp; // 用于交换数据的暂存单元
          for (int i = array.length - 1; i >= 0; i--) { // 将数组最小索引一端视为“水面”
               // 将数组最小索引一端视为“水底”,“气泡”从“水底”向“水面”上浮
               // 因为i每增加1,就有一个上浮到最终排序位置,所以,只需要对1~i个元素进行交换排序
               for (int j = 1; j <= i; j++) {
                    if (array[j - 1] < array[j]) { // 如果上浮过程中发现存在比当前元素小的,就交换,将小的交换到“水面”
                         tmp = array[j - 1];
                         array[j - 1] = array[j];
                         array[j] = tmp;
                    }
               }
          }
     }
}

冒泡排序算法,Python实现,代码如下所示:

class Sorter:
    '''
    Abstract sorter class, which provides shared methods being used by
    subclasses.
    '''
    __metaclass__ = ABCMeta
   
    @abstractmethod   
    def sort(self, array):
        pass

class BubbleSorter(Sorter):
    '''
    Bubble sorter
    '''
    def sort(self, array):
        length = len(array)
        i = length - 1
        while i>=0:
            j = 1
            while j<=i:
                if array[j-1]<array[j]:
                    array[j-1], array[j] = array[j], array[j-1]
                j = j + 1
            i = i - 1


排序过程


冒泡排序的执行过程如下:


首先,将待排序数组视为一个无序区。
从数组一端开始,让元素小的逐步移动到另一端,称为气泡的上浮过程,直到整个数组变成一个有序区。
下面,我们通过例子还说明排序过程。假设待排序数组为array = {94,12,34,76,26,9,0,37,55,76,37,5,68,83,90,37,12,65,76,49},数组大小为20。
将数组最小索引一端视为“水底”,排序过程如下所示:

{94,34,76,26,12,9,37,55,76,37,5,68,83,90,37,12,65,76,49,    0}
{94,76,34,26,12,37,55,76,37,9,68,83,90,37,12,65,76,49,    5,0}
{94,76,34,26,37,55,76,37,12,68,83,90,37,12,65,76,49,    9,5,0}
{94,76,34,37,55,76,37,26,68,83,90,37,12,65,76,49,    12,9,5,0}
{94,76,37,55,76,37,34,68,83,90,37,26,65,76,49,    12,12,9,5,0}
{94,76,55,76,37,37,68,83,90,37,34,65,76,49,    26,12,12,9,5,0}
{94,76,76,55,37,68,83,90,37,37,65,76,49,    34,26,12,12,9,5,0}
{94,76,76,55,68,83,90,37,37,65,76,49,    37,34,26,12,12,9,5,0}
{94,76,76,68,83,90,55,37,65,76,49,    37,37,34,26,12,12,9,5,0}
{94,76,76,83,90,68,55,65,76,49,    37,37,37,34,26,12,12,9,5,0}
{94,76,83,90,76,68,65,76,55,    49,37,37,37,34,26,12,12,9,5,0}
{94,83,90,76,76,68,76,65,    55,49,37,37,37,34,26,12,12,9,5,0}
{94,90,83,76,76,76,68,    65,55,49,37,37,37,34,26,12,12,9,5,0}
{94,90,83,76,76,76,    68,65,55,49,37,37,37,34,26,12,12,9,5,0}
{94,90,83,76,76,    76,68,65,55,49,37,37,37,34,26,12,12,9,5,0}
{94,90,83,76,    76,76,68,65,55,49,37,37,37,34,26,12,12,9,5,0}
{94,90,83,    76,76,76,68,65,55,49,37,37,37,34,26,12,12,9,5,0}
{94,90,    83,76,76,76,68,65,55,49,37,37,37,34,26,12,12,9,5,0}
{94,    90,83,76,76,76,68,65,55,49,37,37,37,34,26,12,12,9,5,0}
{    94,90,83,76,76,76,68,65,55,49,37,37,37,34,26,12,12,9,5,0}

上图是冒泡排序过程中执行各趟排序,整个数组中元素的位置信息:左上半部分是无序区,右下半部分是有序区。


算法分析


时间复杂度
最好情况:有序
数组元素需要两两比较,一趟排序完成。
比较次数:n-1
交换次数:0


最坏情况:逆序
需要进行n-1趟排序。
有序区数组大小为0时:比较n-1次,交换n-1次,移动3(n-1)次;
有序区数组大小为1时:比较n-2次,交换n-2次,移动3(n-2)次;
……
有序区数组大小为n-3时:比较2次,交换2次,移动3*2次;
有序区数组大小为n-2时:比较1次,交换1次,移动3*1次;
比较次数为:1+2+……+(n-1) = n(n-1)/2
移动次数为:3(1+2+……+(n-1)) = 3n(n-1)/2
综上,冒泡排序的时间复杂度为O(n2)。


空间复杂度
冒泡排序属于交换排序,在排序过程中,只需要用到一个用来执行元素交换的变量即可。因此,空间复杂度为O(1)。


排序稳定性
冒泡排序是就地排序。
冒泡排序是稳定的。

冒泡排序-排序过程 设想被排序的数组R[1..N]垂直竖立,将每个数据元素看作有重量的气泡,根据轻气泡不能在重气泡之下的原则,从下往上扫描数组R,凡扫描到违反本原则的轻气泡,就使其向上"漂浮",如此反复进行,直至最后任何两个气泡都是轻者在上,重者在下为止。 算法示例 49 13 13 13 13 13 13 13 38 49 27 27 27 27 27 27 65 38 49 38 38 38 38 38 97 65 38 49 49 49 49 49 76 97 65 49 49 49 49 49 13 76 97 65 65 65 65 65 27 27 76 97 76 76 76 76 49 49 49 76 97 97 97 97 Procedure BubbleSort(Var R : FileType) //从下往上扫描的起泡排序// Begin For I := 1 To N-1 Do //做N-1趟排序// begin NoSwap := True; //置未排序的标志// For J := N - 1 DownTo 1 Do //从底部往上扫描// begin If R[J+1]< R[J] Then //交换元素// begin Temp := R[J+1]; R[J+1 := R[J]; R[J] := Temp; NoSwap := False end; end; If NoSwap Then Return//本趟排序中未发生交换,则终止算法// end End; //BubbleSort// 该算法的时间复杂性为O(n2),算法为稳定的排序方 冒泡排序-冒泡排序法的改进 比如用冒泡排序将4、5、7、1、2、3这6个数排序。在该列中,第二趟排序结束后,数组已排好序,但计算机此时并不知道已经反排好序,计算机还需要进行一趟比较,如果这一趟比较,未发生任何数据交换,则知道已排序好,可以不再进行比较了。因而第三趟比较还需要进行,但第四、五趟比较则是不必要的。为此,我们可以考虑程序的优化。 为了标志在比较中是否进行了,设一个布尔量flag。在进行每趟比较前将flag置成true。如果在比较中发生了数据交换,则将flag置为false,在一趟比较结束后,再判断flag,如果它仍为true(表明在该趟比较中未发生一次数据交换)则结束排序,否则进行下一趟比较。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值