冒泡排序Bubble Sort)是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。

       冒泡排序算法的运作如下:

   1. 比较相邻的元素。如果第一个比第二个大,就交换他们两个。

   2. 对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应        该会是最大的数。

   3. 针对所有的元素重复以上的步骤,除了最后一个。

   4. 持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。


       我们可以利用冒泡法将无序的数列转换成从小到大排列的数列,也可以转换成从小到大排列的数列,为了更加符合冒泡法的名字的含义,下面的算法都是要将数列转换成从小到大排列的数列。

       为了更加清晰的介绍冒泡排序算法,我们先定义一个数组和一个交换数组元素的函数swap( )。

   #define LEN 10

   int number[LEN] = {9, 7, 8, 32, 12, 6, 33, 2, 21, 14};   //无序数列

   void swap(int *s, int i, int j)

   {

         int temp = s[i];

         s[i] = s[j];

         s[j] = temp;

    }

   冒泡法的具体实现可以有很多种,我们介绍以下几种算法。

   1. BubbleSort_0

void BubbleSort_0(int *s, int n)
{
    int i, j;
    for(i = 0; i != n - 1; i++)
    {
        for(j = i + 1; j != n; j++)
        {
            if(s[i] > s[j])
            {
                swap(s, i, j);
            }
        }
    }
}

   下面是使用该算法对数列进行排序的输出,为了更清晰的理解该算法,我将每一次的排序结果进行输出,最后一次排序后,数列就已经变成了从小到大排列的数列。

   wKiom1NIpkbTNQXHAAEUOXAm4e0044.jpg

   从上面的输出可以看出,该算法每一次都将未排好序的数列最小的数放在未排好序的数列的第一个,譬如第一趟将2放在第一个,2后面的数列就是未排好序的数列;第二趟将6放在未排好序数列的第一个,以此类推直到所有的数都排好,整个数列就是一个从小到大排列的数列。

   严格意义上讲,这个算法并不是冒泡算法,因为它不是比较相邻的数。它只是一个简单的交换排序而已,在每一趟的排序中,将未排好序的数列的第一个数与它后面的每一个数相比较,如果大于后面的数,就将这两个数交换,最后这个未排好序的数列的第一个数就是最小值。下面介绍正宗的冒泡法。


   2. BubbleSort

void BubbleSort(int *s, int n)
{
    int i, j;
    for(i = 0; i != n - 1; i++)
    {
        for(j = 0; j != n - i - 1; j++)
        {
            if(s[j] > s[j+1])
            {
                swap(s, j, j+1);
            }
        }
    }
}

   对于同样输入的输出:

    wKiom1NIrH3jAE1mAAEPO_iu3Z8490.jpg

   根据冒泡法的思想,每一趟排序都是两个相邻的数比较,将较大的数往后移动,因此每一趟排序最后的数都是这趟排序中最大的一个数。

   对于这个算法,要注意几点:

   1)第一个for循环的终止条件: i != n - 1;

      首先 i 是从 0 开始的,n 是数列元素的个数,该终止条件就是说只用循环n - 1趟即可。因为第 1 趟时,i为0;那么第n - 1趟的时候,i为n - 2, 里面的for循环的终止条件是j != 1,也就是说这趟循环只用比较s[0]和s[1],并且会排好顺序,整个数列也就排好了顺序。那么第10趟就可以省略了。

   2)第二个for循环的终止条件:j != n - i - 1;

      n个数组成的序列有n - 1对相邻的数,第1对j = 0,比较s[0]和s[1], 第2对j = 1,比较s[1]和s[2],....,一直到第 n-1 对,j = n - 2,比较s[n-2]和s[n-1](最后一项),因为每一趟都会确定一个最大的数放在数列末尾,所以每一趟就会少一对数,所以就是 n - i - 1(i每1趟都会加1)。

      如果将终止条件改为j != n - i;那么每一趟就会多比较一次,对于第一趟,最后将会比较数列的最后一个数和数组外的一个数,显然算法就有问题了。

   3)注意上面的输出,其实在第7趟时数列就已经排好顺序了,可是算法还是要多执行两次循环,显然这里是可以优化的,所以可以优化为下面的算法。

   

  3. 可以利用一个标记,对冒泡法进行优化。

void BubbleSort(int *s, int n)
{
    int i, j;
    int flag = 1;  //flag为1表示还有进行交换,为0表示没有再交换
    for(i = 0; i != n - 1 && flag; i++)
    {
        flag = 0;
        for(j = 0; j != n - i - 1; j++)
        {
            if(s[j] > s[j+1])
            {
                swap(s, j, j+1);
                flag = 1;
            }
        }
    }
}

    输出如下:

    wKioL1NIvwOQQlIqAAD592RMgtE038.jpg

    可以看出,输出中少了一次循环。因为每次排好序之后还要循环一次才能确定是否真的排好序了,所以可以看到第七次排好序之后还有第8次的循环。