按字典序輸出陣列的全排列
阿新 • • 發佈:2019-01-27
對於每個用例,輸出它的全排列,每個排列佔一行,輸出按照數值升序排列
思路:
我定義了一個函式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); } } }