OpenDSA 完整目录

Chapter 12 Sorting

| 关于   «  3. 插入排序   ::   目录   ::   5. 选择排序  »

4. 冒泡排序

4.1. 冒泡排序

我们的下一个排序算法叫做 冒泡排序 。 冒泡排序常被用来教计算机科学导论课上的初学者。 这很遗憾,因为冒泡排序毫无可取之处。 与其他常见的 \(\Theta(n^2)\) 排序算法相比,它相当慢。 它也谈不上直观——没有人在整理桥牌手牌或一堆账单时,会像 想到 插入排序 或 选择排序 那样, 自然而然地想到冒泡排序。 不过,冒泡排序可以看作选择排序的近亲。

与插入排序类似,冒泡排序由简单的双重 for 循环构成。 内层 for 循环从左到右扫过记录数组,比较相邻的键。 如果一条记录的键值大于其右邻记录的键,就交换这两条 记录。 一旦遇到键值最大的记录,这个过程会让它"冒泡"到数组的 右端(冒泡排序因此得名)。 第二趟重复这个过程。 不过,由于我们已经知道键最大的记录在第一趟就到了数组 右端,第二趟无需再比较最右边的两条记录。 同样地,之后每一趟都比较相邻记录,只是比上一趟少看末尾的 一条记录。 下面是一种实现。

void bubblesort(int[] A) {
  for (int i=0; i<A.length-1; i++) // Insert i'th record
    for (int j=1; j<A.length-i; j++)
      if (A[j-1] > A[j])
        swap(A, j-1, j);
}
void bubblesort(T[] A) {
  for (int i=0; i<A.length-1; i++) // Insert i'th record
    for (int j=1; j<A.length-i; j++)
      if (A[j-1].compareTo(A[j]) > 0)
        swap(A, j-1, j);
}
void bubblesort(Comparable* A[], int n) {
  for (int i = 0; i < n-1; i++) // Insert i'th record
    for (int j = 0; j < n-i; j++) 
      if (*A[j] > *A[j+1]) 
        swap(A, j, j+1);
}

Settings

Proficient Saving... Error Saving
Server Error
Resubmit

现在继续第二趟。不过,由于最大的记录已经"冒泡"到最右端, 我们不必再看它。

Settings

Proficient Saving... Error Saving
Server Error
Resubmit

冒泡排序就这样一直进行下去,直到整个数组有序。

下面的可视化展示完整的冒泡排序过程。 如果愿意,你可以输入自己的数据。

现在请自己试一试,看看你是否理解了冒泡排序的工作原理。

4.2. 冒泡排序分析

下面的可视化展示冒泡排序的运行时间分析。

Settings

Proficient Saving... Error Saving
Server Error
Resubmit

因此,冒泡排序在最好、平均和最坏情况下的运行时间大致相同。

所需的交换次数取决于"某条记录的值小于数组中紧邻其前的 记录"这种情况发生的频率。 平均情况下,我们可以预期这种情况约占比较次数的一半,于是 期望交换次数为 \(\Theta(n^2)\) 。 冒泡排序实际执行的交换次数与插入排序完全相同。

下面是一些帮助你检查冒泡排序理解程度的复习题。

   «  3. 插入排序   ::   目录   ::   5. 选择排序  »

关闭窗口