參考連結: 全排列產生演算法(一)
原文講的很詳細了,為了完整性,這裡粘貼的參考連結中大部分文字,並且在原文的基礎上,添加了“給定某個排列,求其字典序中上一個元素”的演算法。
字典序
全排列產生演算法的一個重要思路,就是將集合A中的元素的排列,與某種順序建立一一映射的關係,按照這種順序,將集合的所有排列全部輸出。這種順序需要保證,既可以輸出全部的排列,又不能重複輸出某種排列,或者迴圈輸出一部分排列。字典序就是用此種思想輸出全排列的一種方式。這裡以A{1,2,3,4}來說明用字典序輸出全排列的方法。
首先,對於集合A的某種排列所形成的序列,字典序是比較序列大小的一種方式。以A{1,2,3,4}為例,其所形成的排列1234<1243,比較的方法是從前到後依次比較兩個序列的對應元素,如果當前位置對應元素相同,則繼續比較下一個位置,直到第一個元素不同的位置為止,元素值大的元素在字典序中就大於元素值小的元素。上面的a1[1...4]=1234和a2[1...4]=1243,對於i=1,i=2,兩序列的對應元素相等,但是當i=2時,有a1[2]=3<a2[2]=4,所以1234<1243。
求字典序的下一個全排列:使用字典序輸出全排列的思路是,首先輸出字典序最小的排列,然後輸出字典序次小的排列,……,最後輸出字典序最大的排列
。這裡就涉及到一個問題,對於一個已知排列,如何求出其字典序中的下一個排列
。這裡給出演算法。
- 對於排列a[1...n],找到所有滿足a[k]<a[k+1](0<k<n-1)的k的最大值,如果這樣的k不存在,則說明當前排列已經是a的所有排列中字典序最大者,所有排列輸出完畢。
- 在a[k+1...n]中,尋找滿足這樣條件的元素l,使得在所有a[l]>a[k]的元素中,a[l]取得最小值。也就是說a[l]>a[k],但是小於所有其他大於a[k]的元素。
- 交換a[l]與a[k].
- 對於a[k+1...n],反轉該區間內元素的順序。也就是說a[k+1]與a[n]交換,a[k+2]與a[n-1]交換,……,這樣就得到了a[1...n]在字典序中的下一個排列。
這裡我們以排列a[1..
.8]=13876542為例,來解釋一下上述演算法。首先我們發現,1(38)76542,括弧位置是第一處滿足a[k]<a[k+1]的位置,此時k=2
。所以我們在a[3...8]的區間內尋找比a[2]=3大的最小元素,找到a[7]=4滿足條件,交換a[2]和a[7]得到新排列14876532,對於此排列的3~8區間,反轉該區間的元素,將a[3]-a[8],a[4]-a[7],a[5]-a[6]分別交換,就得到了13876542字典序的下一個元素14235678
。
求字典序的上一個全排列:從前面的的分析,我們可以進行逆推導,求得上一個排列,這裡的演算法如下:1. 對於排列a[1..n],從尾端開始,找到第一個a[k]>a[k+1]並記錄,若k<0,則說明a已經是字典序的最小者。2. 在a[k+1..n]中,尋找小於a[k]的最大元素a[i],3. 交換a[k]和a[i]的值,4. 對於a[k+1..n],反轉區間內元素的順序。這裡我們還是以前面的例子來說明,初始時,a=14235678,首先找到1(42)35678,此時k=2,再找到比4小的最大數是3,此時a[i]=3, i=4, 交換a[i]和a[k]的值,得到a=13245678,最後反轉a[k+1..n],得到最後的結果a=13876542。
C代碼如下:
/*http://t.jobdu.com/thread-98707-1-1.html1、寫出一個演算法來產生1-n的全排列2、已知一個長度為N的序列A,它的每個元素是1-N之間的任何一個元素,且兩兩不重複,稱他為一個排列,寫出一個演算法產生該排列的字典序的下一排列。例如,A=[3 2 1 4],則下一排列為A'=[3 2 4 1],A'的下一排列為A''=[3 4 1 2],以此類推。http://blog.csdn.net/joylnwang/article/details/7064115*/#include <stdio.h>void swap(char* array, unsigned int i, unsigned int j){ char t = array[i]; array[i] = array[j]; array[j] = t;}// 遞迴輸出序列的全排列void fullPermutation(char* arr, int len, int index){ int i; if (index >= len) printf("%s\n", arr); else { for (i = index; i < len; i++) { swap(arr, index, i); fullPermutation(arr, len, index+1); swap(arr, index, i); } }}void reverse(char arr[], int start, int end){ if (start >= end) return; int len = start - end + 1; int i; for (i = 0; i <= len/2; i++) { swap(arr, start+i, end-i); }}int pre_permutation(char arr[], int len){ int k, i, max, max_i; for (i = len-2; i >= 0; i--) { if (arr[i] > arr[i+1]) { k = i; break; } } if (i < 0) { printf("%s is the first permutation\n", arr); return -1; } max_i = k + 1; max = arr[max_i]; for (i = k+1; i < len; i++) { if (arr[i] < arr[k] && arr[i] > max) { max_i = i; max = arr[max_i]; } } if (max_i < len) { swap(arr, k, max_i); reverse(arr, k+1, len-1); } return 0;}int next_permutation(char arr[], int len){ int k, i, min, min_i; for (k = len-2; k >= 0; k--) { if (arr[k] < arr[k+1]) break; } if (k < 0) { printf("%s is the last permutation\n", arr); return -1; } min = arr[k+1]; min_i = k+1; for (i = k + 1; i < len; i++) { if (arr[i] > arr[k] && arr[i] < min) { min_i = i; min = arr[i]; } } if (min_i < len) { swap(arr, k, min_i); reverse(arr, k+1, len-1); } return 0;}int main(){ int i = 1; char arr[] = "1234"; int len = sizeof(arr) / sizeof(arr[0]) - 1; //fullPermutation(arr, len, 0); // 遞迴列印全排列 // 按照字典序輸出全排列 printf("next_permutation demo:\n"); do { printf("%02d: %s\n", i, arr); i++; } while (next_permutation(arr, len) >= 0); i = 1; printf("\npre_permutation demo:\n"); do { printf("%02d: %s\n", i, arr); i++; } while (pre_permutation(arr, len) >= 0); return 0;}