氣泡排序

極為常見

排序的概念

排序,顧名思義就是將一群數字根據規則排列。在排序中我們通常會使用升序來排列,當然也可以不是,但就需要自定義一些排序規則。假設我們有一個陣列,當中的數據是長這樣: {5, 3, 6, 1, 2} 。現在我們想要將它做升序排列,我們會期待排序後的結果是 {1, 2, 3, 5, 6}

泡沫排序

在許多排序方法中,較廣為人知且較容易學習的是「泡沫排序法(Bubble Sort)」。排序過程中,泡沫排序法會重複地比較相鄰的兩個數字,若前面的數字比後面的數字大,則交換這兩個數字的位置,直到整個陣列排序完成。更具體一點的說,泡沫排序法的流程如下:

  • 整個排序過程分成 n-1 輪,每一輪都會將最大的數字排到最後面。
  • 每一輪排序過程中,都會從陣列的第一個數字開始,比較相鄰的兩個數字,若前面的數字比後面的數字大,則交換這兩個數字的位置,直到整個陣列排序完成。

img

#include <bits/stdc++.h>
using namespace std;

vector<int> v = {5, 3, 6, 1, 2};

int main() {
    // 重複 n-1 輪
    for (int i = v.size() - 1; i > 0; i--) {
        // 每一輪都會掃過 0 到 i 的數字
        for (int j = 0; j < i; j++) {
            // 比較相鄰兩個數字,如果前面的數字比後面的數字大,則交換位置
            if (v[j] > v[j+1]) {
                swap(v[j], v[j+1]);
            }
        }
    }

    // 輸出排序後的結果
    for (auto i : v) {
        cout << i << " ";
    }
}

淺談逆序數對

氣泡排序法的一個重要應用是計算逆序數對。先以升序排列為例,逆序數對是指在一個數列中,若兩個數字 a[i]a[j] 滿足 i < ja[i] > a[j],則這兩個數字就構成一個逆序數對。

看看下面這張圖的例子,在這個數列中,標記起來的 3 和 1 就是逆序數對。

alt text

這個 3 和 2 也是逆序數對。

alt text

而 3 和 4 這對就不是逆序數對。

alt text

那逆序數對跟氣泡排序有什麼關係呢?其實逆序數對就是在氣泡排序的過程中交換的次數!

為什麼逆序數對就是交換次數?

首先我們可以發現,當我們在氣泡排序的過程中,只有當 a[i]a[j] 滿足 i < ja[i] > a[j] 時,我們才會進行交換,也就是說這樣每交換一次就會減少一個數列中的逆序數對。並且 a[i]a[j] 交換時也不會影響到其他的逆序數對。因此逆序數對就是交換次數。

也就是說,我們只要在氣泡排序的程式碼裡面加上計算交換次數的部分,就可以計算出逆序數對了!

#include <bits/stdc++.h>
using namespace std;

vector<int> v = {5, 3, 6, 1, 2};

int main() {
    int ans = 0; // 逆序數對的數量

    for (int i = v.size() - 1; i > 0; i--) {
        for (int j = 0; j < i; j++) {
            if (v[j] > v[j+1]) {
                ans++; // 每交換一次就增加一個逆序數對
                swap(v[j], v[j+1]);
            }
        }
    }

    for (auto i : v) {
        cout << i << " ";
    }

    cout << endl << ans << endl; // 輸出逆序數對的數量
}

雖然氣泡排序相較其他演算法很慢,逆序數對也聽起來很沒用,不過這些將會成為以後一些重要演算法的基礎,且 APCS 的觀念題也常常會出現類似的變化題。

小測驗

氣泡排序和逆序數對的關係是什麼?