1. 程式人生 > >按字典序輸出陣列的全排列

按字典序輸出陣列的全排列

 對於每個用例,輸出它的全排列,每個排列佔一行,輸出按照數值升序排列

思路:

我定義了一個函式next_permutation來計算下一字典序,演算法如下:

1)先從後往前找到一對升序組,設其座標為a和a+1

2)從a+1到陣列末尾找到一個大於a的最小的數,設其座標為b

3)交換a和b位置的值

4)a+1位置到末尾之間的陣列逆序

例:12354

1)從後往前找到第一對升序組(3, 5),若陣列從0開始儲存,這裡a=2,c=3

2)從陣列座標為3的地方(值為5)開始到結束(值為4),找到b=4(值為4)

3)交換a和b位置的值,即交換3和4,陣列變為 12453

4)陣列第三個位置到末尾(即3到4)逆序,陣列變為12435

這樣就找到了陣列的下一個字典序

有了next_permutation函式,就可以從1到n!迴圈,每次輸出當前順序並計算下一字典序。

歷程:

開始寫的是遞迴的形式,就是在next_permulation函式尾部遞迴呼叫自己,但會造成爆棧,所以改成了迴圈。

程式碼:

#include <iostream>
using namespace std;

//計算n的階乘
int factorial(int n) {
    int result = 1;
    for (int i = 2; i <= n; i++) {
        result *= i;
    }
    return result;
}

//改變原陣列的順序為下一個字典序
void next_permutation(int* arr, int length) {
    int a = -1, b = -1;
    int min_ = 100;
    //找到a,a的含義見思路
    for (int i = length - 2; i >= 0; i--) {
        if (arr[i] < arr[i + 1]) {
            a = i;
            break;
        }
    }
    // a=-1意味著陣列是逆序的,不存在下一字典序,故返回
    if (a == -1) return;
    //找b
    for (int i = a + 1; i < length; i++) {
        if (arr[i] > arr[a] && arr[i] < min_) {
            b = i;
            min_ = arr[i];
        }
    }
    //交換a,b
    int temp = arr[a];
    arr[a] = arr[b];
    arr[b] = temp;
    //a+1到末尾陣列逆序
    for (int i = a + 1; i <= (a + length - 1)/2; i++) {
        temp = arr[i];
        arr[i] = arr[a + length - i];
        arr[a + length - i] = temp;
    }
}

int main(void) {
    int m, n;
    int arr[11];
    cin >> m;
    while (m--) {
        cin >> n;
        //按升序初始化陣列
        for (int i = 0; i < n; i++) {
            arr[i] = i + 1;
        }
        //全排列數一共是n!,每個先列印結果,再改變陣列至下一字典序
        for (int i = 1; i <= factorial(n); i++) {
            for (int i = 0; i < n; i++) {
                cout << arr[i];
            }
            cout << endl;
            if (i != factorial(n))
                next_permutation(arr, n);
        }
    }
}