跳轉至

11.3   泡沫排序

泡沫排序(bubble sort)透過連續地比較與交換相鄰元素實現排序。這個過程就像氣泡從底部升到頂部一樣,因此得名泡沫排序。

如圖 11-4 所示,冒泡過程可以利用元素交換操作來模擬:從陣列最左端開始向右走訪,依次比較相鄰元素大小,如果“左元素 > 右元素”就交換二者。走訪完成後,最大的元素會被移動到陣列的最右端。

利用元素交換操作模擬冒泡

bubble_operation_step2

bubble_operation_step3

bubble_operation_step4

bubble_operation_step5

bubble_operation_step6

bubble_operation_step7

圖 11-4   利用元素交換操作模擬冒泡

11.3.1   演算法流程

設陣列的長度為 \(n\) ,泡沫排序的步驟如圖 11-5 所示。

  1. 首先,對 \(n\) 個元素執行“冒泡”,將陣列的最大元素交換至正確位置
  2. 接下來,對剩餘 \(n - 1\) 個元素執行“冒泡”,將第二大元素交換至正確位置
  3. 以此類推,經過 \(n - 1\) 輪“冒泡”後,\(n - 1\) 大的元素都被交換至正確位置
  4. 僅剩的一個元素必定是最小元素,無須排序,因此陣列排序完成。

泡沫排序流程

圖 11-5   泡沫排序流程

示例程式碼如下:

bubble_sort.py
def bubble_sort(nums: list[int]):
    """泡沫排序"""
    n = len(nums)
    # 外迴圈:未排序區間為 [0, i]
    for i in range(n - 1, 0, -1):
        # 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
        for j in range(i):
            if nums[j] > nums[j + 1]:
                # 交換 nums[j] 與 nums[j + 1]
                nums[j], nums[j + 1] = nums[j + 1], nums[j]
bubble_sort.cpp
/* 泡沫排序 */
void bubbleSort(vector<int> &nums) {
    // 外迴圈:未排序區間為 [0, i]
    for (int i = nums.size() - 1; i > 0; i--) {
        // 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
        for (int j = 0; j < i; j++) {
            if (nums[j] > nums[j + 1]) {
                // 交換 nums[j] 與 nums[j + 1]
                // 這裡使用了 std::swap() 函式
                swap(nums[j], nums[j + 1]);
            }
        }
    }
}
bubble_sort.java
/* 泡沫排序 */
void bubbleSort(int[] nums) {
    // 外迴圈:未排序區間為 [0, i]
    for (int i = nums.length - 1; i > 0; i--) {
        // 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
        for (int j = 0; j < i; j++) {
            if (nums[j] > nums[j + 1]) {
                // 交換 nums[j] 與 nums[j + 1]
                int tmp = nums[j];
                nums[j] = nums[j + 1];
                nums[j + 1] = tmp;
            }
        }
    }
}
bubble_sort.cs
/* 泡沫排序 */
void BubbleSort(int[] nums) {
    // 外迴圈:未排序區間為 [0, i]
    for (int i = nums.Length - 1; i > 0; i--) {
        // 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
        for (int j = 0; j < i; j++) {
            if (nums[j] > nums[j + 1]) {
                // 交換 nums[j] 與 nums[j + 1]
                (nums[j + 1], nums[j]) = (nums[j], nums[j + 1]);
            }
        }
    }
}
bubble_sort.go
/* 泡沫排序 */
func bubbleSort(nums []int) {
    // 外迴圈:未排序區間為 [0, i]
    for i := len(nums) - 1; i > 0; i-- {
        // 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
        for j := 0; j < i; j++ {
            if nums[j] > nums[j+1] {
                // 交換 nums[j] 與 nums[j + 1]
                nums[j], nums[j+1] = nums[j+1], nums[j]
            }
        }
    }
}
bubble_sort.swift
/* 泡沫排序 */
func bubbleSort(nums: inout [Int]) {
    // 外迴圈:未排序區間為 [0, i]
    for i in nums.indices.dropFirst().reversed() {
        // 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
        for j in 0 ..< i {
            if nums[j] > nums[j + 1] {
                // 交換 nums[j] 與 nums[j + 1]
                nums.swapAt(j, j + 1)
            }
        }
    }
}
bubble_sort.js
/* 泡沫排序 */
function bubbleSort(nums) {
    // 外迴圈:未排序區間為 [0, i]
    for (let i = nums.length - 1; i > 0; i--) {
        // 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
        for (let j = 0; j < i; j++) {
            if (nums[j] > nums[j + 1]) {
                // 交換 nums[j] 與 nums[j + 1]
                let tmp = nums[j];
                nums[j] = nums[j + 1];
                nums[j + 1] = tmp;
            }
        }
    }
}
bubble_sort.ts
/* 泡沫排序 */
function bubbleSort(nums: number[]): void {
    // 外迴圈:未排序區間為 [0, i]
    for (let i = nums.length - 1; i > 0; i--) {
        // 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
        for (let j = 0; j < i; j++) {
            if (nums[j] > nums[j + 1]) {
                // 交換 nums[j] 與 nums[j + 1]
                let tmp = nums[j];
                nums[j] = nums[j + 1];
                nums[j + 1] = tmp;
            }
        }
    }
}
bubble_sort.dart
/* 泡沫排序 */
void bubbleSort(List<int> nums) {
  // 外迴圈:未排序區間為 [0, i]
  for (int i = nums.length - 1; i > 0; i--) {
    // 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
    for (int j = 0; j < i; j++) {
      if (nums[j] > nums[j + 1]) {
        // 交換 nums[j] 與 nums[j + 1]
        int tmp = nums[j];
        nums[j] = nums[j + 1];
        nums[j + 1] = tmp;
      }
    }
  }
}
bubble_sort.rs
/* 泡沫排序 */
fn bubble_sort(nums: &mut [i32]) {
    // 外迴圈:未排序區間為 [0, i]
    for i in (1..nums.len()).rev() {
        // 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
        for j in 0..i {
            if nums[j] > nums[j + 1] {
                // 交換 nums[j] 與 nums[j + 1]
                nums.swap(j, j + 1);
            }
        }
    }
}
bubble_sort.c
/* 泡沫排序 */
void bubbleSort(int nums[], int size) {
    // 外迴圈:未排序區間為 [0, i]
    for (int i = size - 1; i > 0; i--) {
        // 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
        for (int j = 0; j < i; j++) {
            if (nums[j] > nums[j + 1]) {
                int temp = nums[j];
                nums[j] = nums[j + 1];
                nums[j + 1] = temp;
            }
        }
    }
}
bubble_sort.kt
/* 泡沫排序 */
fun bubbleSort(nums: IntArray) {
    // 外迴圈:未排序區間為 [0, i]
    for (i in nums.size - 1 downTo 1) {
        // 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
        for (j in 0..<i) {
            if (nums[j] > nums[j + 1]) {
                // 交換 nums[j] 與 nums[j + 1]
                val temp = nums[j]
                nums[j] = nums[j + 1]
                nums[j + 1] = temp
            }
        }
    }
}
bubble_sort.rb
### 泡沫排序 ###
def bubble_sort(nums)
  n = nums.length
  # 外迴圈:未排序區間為 [0, i]
  for i in (n - 1).downto(1)
    # 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
    for j in 0...i
      if nums[j] > nums[j + 1]
        # 交換 nums[j] 與 nums[j + 1]
        nums[j], nums[j + 1] = nums[j + 1], nums[j]
      end
    end
  end
end
bubble_sort.zig
// 泡沫排序
fn bubbleSort(nums: []i32) void {
    // 外迴圈:未排序區間為 [0, i]
    var i: usize = nums.len - 1;
    while (i > 0) : (i -= 1) {
        var j: usize = 0;
        // 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
        while (j < i) : (j += 1) {
            if (nums[j] > nums[j + 1]) {
                // 交換 nums[j] 與 nums[j + 1]
                var tmp = nums[j];
                nums[j] = nums[j + 1];
                nums[j + 1] = tmp;
            }
        }
    }
}
視覺化執行

11.3.2   效率最佳化

我們發現,如果某輪“冒泡”中沒有執行任何交換操作,說明陣列已經完成排序,可直接返回結果。因此,可以增加一個標誌位 flag 來監測這種情況,一旦出現就立即返回。

經過最佳化,泡沫排序的最差時間複雜度和平均時間複雜度仍為 \(O(n^2)\) ;但當輸入陣列完全有序時,可達到最佳時間複雜度 \(O(n)\)

bubble_sort.py
def bubble_sort_with_flag(nums: list[int]):
    """泡沫排序(標誌最佳化)"""
    n = len(nums)
    # 外迴圈:未排序區間為 [0, i]
    for i in range(n - 1, 0, -1):
        flag = False  # 初始化標誌位
        # 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
        for j in range(i):
            if nums[j] > nums[j + 1]:
                # 交換 nums[j] 與 nums[j + 1]
                nums[j], nums[j + 1] = nums[j + 1], nums[j]
                flag = True  # 記錄交換元素
        if not flag:
            break  # 此輪“冒泡”未交換任何元素,直接跳出
bubble_sort.cpp
/* 泡沫排序(標誌最佳化)*/
void bubbleSortWithFlag(vector<int> &nums) {
    // 外迴圈:未排序區間為 [0, i]
    for (int i = nums.size() - 1; i > 0; i--) {
        bool flag = false; // 初始化標誌位
        // 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
        for (int j = 0; j < i; j++) {
            if (nums[j] > nums[j + 1]) {
                // 交換 nums[j] 與 nums[j + 1]
                // 這裡使用了 std::swap() 函式
                swap(nums[j], nums[j + 1]);
                flag = true; // 記錄交換元素
            }
        }
        if (!flag)
            break; // 此輪“冒泡”未交換任何元素,直接跳出
    }
}
bubble_sort.java
/* 泡沫排序(標誌最佳化) */
void bubbleSortWithFlag(int[] nums) {
    // 外迴圈:未排序區間為 [0, i]
    for (int i = nums.length - 1; i > 0; i--) {
        boolean flag = false; // 初始化標誌位
        // 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
        for (int j = 0; j < i; j++) {
            if (nums[j] > nums[j + 1]) {
                // 交換 nums[j] 與 nums[j + 1]
                int tmp = nums[j];
                nums[j] = nums[j + 1];
                nums[j + 1] = tmp;
                flag = true; // 記錄交換元素
            }
        }
        if (!flag)
            break; // 此輪“冒泡”未交換任何元素,直接跳出
    }
}
bubble_sort.cs
/* 泡沫排序(標誌最佳化)*/
void BubbleSortWithFlag(int[] nums) {
    // 外迴圈:未排序區間為 [0, i]
    for (int i = nums.Length - 1; i > 0; i--) {
        bool flag = false; // 初始化標誌位
        // 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
        for (int j = 0; j < i; j++) {
            if (nums[j] > nums[j + 1]) {
                // 交換 nums[j] 與 nums[j + 1]
                (nums[j + 1], nums[j]) = (nums[j], nums[j + 1]);
                flag = true;  // 記錄交換元素
            }
        }
        if (!flag) break;     // 此輪“冒泡”未交換任何元素,直接跳出
    }
}
bubble_sort.go
/* 泡沫排序(標誌最佳化)*/
func bubbleSortWithFlag(nums []int) {
    // 外迴圈:未排序區間為 [0, i]
    for i := len(nums) - 1; i > 0; i-- {
        flag := false // 初始化標誌位
        // 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
        for j := 0; j < i; j++ {
            if nums[j] > nums[j+1] {
                // 交換 nums[j] 與 nums[j + 1]
                nums[j], nums[j+1] = nums[j+1], nums[j]
                flag = true // 記錄交換元素
            }
        }
        if flag == false { // 此輪“冒泡”未交換任何元素,直接跳出
            break
        }
    }
}
bubble_sort.swift
/* 泡沫排序(標誌最佳化)*/
func bubbleSortWithFlag(nums: inout [Int]) {
    // 外迴圈:未排序區間為 [0, i]
    for i in nums.indices.dropFirst().reversed() {
        var flag = false // 初始化標誌位
        for j in 0 ..< i {
            if nums[j] > nums[j + 1] {
                // 交換 nums[j] 與 nums[j + 1]
                nums.swapAt(j, j + 1)
                flag = true // 記錄交換元素
            }
        }
        if !flag { // 此輪“冒泡”未交換任何元素,直接跳出
            break
        }
    }
}
bubble_sort.js
/* 泡沫排序(標誌最佳化)*/
function bubbleSortWithFlag(nums) {
    // 外迴圈:未排序區間為 [0, i]
    for (let i = nums.length - 1; i > 0; i--) {
        let flag = false; // 初始化標誌位
        // 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
        for (let j = 0; j < i; j++) {
            if (nums[j] > nums[j + 1]) {
                // 交換 nums[j] 與 nums[j + 1]
                let tmp = nums[j];
                nums[j] = nums[j + 1];
                nums[j + 1] = tmp;
                flag = true; // 記錄交換元素
            }
        }
        if (!flag) break; // 此輪“冒泡”未交換任何元素,直接跳出
    }
}
bubble_sort.ts
/* 泡沫排序(標誌最佳化)*/
function bubbleSortWithFlag(nums: number[]): void {
    // 外迴圈:未排序區間為 [0, i]
    for (let i = nums.length - 1; i > 0; i--) {
        let flag = false; // 初始化標誌位
        // 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
        for (let j = 0; j < i; j++) {
            if (nums[j] > nums[j + 1]) {
                // 交換 nums[j] 與 nums[j + 1]
                let tmp = nums[j];
                nums[j] = nums[j + 1];
                nums[j + 1] = tmp;
                flag = true; // 記錄交換元素
            }
        }
        if (!flag) break; // 此輪“冒泡”未交換任何元素,直接跳出
    }
}
bubble_sort.dart
/* 泡沫排序(標誌最佳化)*/
void bubbleSortWithFlag(List<int> nums) {
  // 外迴圈:未排序區間為 [0, i]
  for (int i = nums.length - 1; i > 0; i--) {
    bool flag = false; // 初始化標誌位
    // 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
    for (int j = 0; j < i; j++) {
      if (nums[j] > nums[j + 1]) {
        // 交換 nums[j] 與 nums[j + 1]
        int tmp = nums[j];
        nums[j] = nums[j + 1];
        nums[j + 1] = tmp;
        flag = true; // 記錄交換元素
      }
    }
    if (!flag) break; // 此輪“冒泡”未交換任何元素,直接跳出
  }
}
bubble_sort.rs
/* 泡沫排序(標誌最佳化) */
fn bubble_sort_with_flag(nums: &mut [i32]) {
    // 外迴圈:未排序區間為 [0, i]
    for i in (1..nums.len()).rev() {
        let mut flag = false; // 初始化標誌位
        // 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
        for j in 0..i {
            if nums[j] > nums[j + 1] {
                // 交換 nums[j] 與 nums[j + 1]
                nums.swap(j, j + 1);
                flag = true; // 記錄交換元素
            }
        }
        if !flag {
            break; // 此輪“冒泡”未交換任何元素,直接跳出
        };
    }
}
bubble_sort.c
/* 泡沫排序(標誌最佳化)*/
void bubbleSortWithFlag(int nums[], int size) {
    // 外迴圈:未排序區間為 [0, i]
    for (int i = size - 1; i > 0; i--) {
        bool flag = false;
        // 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
        for (int j = 0; j < i; j++) {
            if (nums[j] > nums[j + 1]) {
                int temp = nums[j];
                nums[j] = nums[j + 1];
                nums[j + 1] = temp;
                flag = true;
            }
        }
        if (!flag)
            break;
    }
}
bubble_sort.kt
/* 泡沫排序(標誌最佳化) */
fun bubbleSortWithFlag(nums: IntArray) {
    // 外迴圈:未排序區間為 [0, i]
    for (i in nums.size - 1 downTo 1) {
        var flag = false // 初始化標誌位
        // 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
        for (j in 0..<i) {
            if (nums[j] > nums[j + 1]) {
                // 交換 nums[j] 與 nums[j + 1]
                val temp = nums[j]
                nums[j] = nums[j + 1]
                nums[j + 1] = temp
                flag = true // 記錄交換元素
            }
        }
        if (!flag) break // 此輪“冒泡”未交換任何元素,直接跳出
    }
}
bubble_sort.rb
### 泡沫排序(標誌最佳化)###
def bubble_sort_with_flag(nums)
  n = nums.length
  # 外迴圈:未排序區間為 [0, i]
  for i in (n - 1).downto(1)
    flag = false # 初始化標誌位

    # 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
    for j in 0...i
      if nums[j] > nums[j + 1]
        # 交換 nums[j] 與 nums[j + 1]
        nums[j], nums[j + 1] = nums[j + 1], nums[j]
        flag = true # 記錄交換元素
      end
    end

    break unless flag # 此輪“冒泡”未交換任何元素,直接跳出
  end
end
bubble_sort.zig
// 泡沫排序(標誌最佳化)
fn bubbleSortWithFlag(nums: []i32) void {
    // 外迴圈:未排序區間為 [0, i]
    var i: usize = nums.len - 1;
    while (i > 0) : (i -= 1) {
        var flag = false;   // 初始化標誌位
        var j: usize = 0;
        // 內迴圈:將未排序區間 [0, i] 中的最大元素交換至該區間的最右端
        while (j < i) : (j += 1) {
            if (nums[j] > nums[j + 1]) {
                // 交換 nums[j] 與 nums[j + 1]
                var tmp = nums[j];
                nums[j] = nums[j + 1];
                nums[j + 1] = tmp;
                flag = true;
            }
        }
        if (!flag) break;   // 此輪“冒泡”未交換任何元素,直接跳出
    }
}
視覺化執行

11.3.3   演算法特性

  • 時間複雜度為 \(O(n^2)\)、自適應排序:各輪“冒泡”走訪的陣列長度依次為 \(n - 1\)\(n - 2\)\(\dots\)\(2\)\(1\) ,總和為 \((n - 1) n / 2\) 。在引入 flag 最佳化後,最佳時間複雜度可達到 \(O(n)\)
  • 空間複雜度為 \(O(1)\)、原地排序:指標 \(i\)\(j\) 使用常數大小的額外空間。
  • 穩定排序:由於在“冒泡”中遇到相等元素不交換。