參考於:【STL】next_permutation的原理和使用
給定一個數列,如何得到它的全排列?
例如1 2 3,它的全排列是123,132,213,231,312,321。
全排列的關鍵在於,給定某一數列,能從該數列推出“下一個”數列。
那么如何找“下一個”數列呢?
找“下一個”數列的算法思路描述如下:
1、從后往前找兩個相鄰元素,前一個位置記為i,后一個記為j,並且滿足s[i] < s[j]
2、從后往前找另一個位置k,若滿足s[i] < s[k],則將s[i]與s[k]交換,並將j之后(包括j)的所有元素顛倒排序,即得“下一個”數列。
先試着自己按照這個算法思路去模擬一下算法過程,看能不能悟出這個算法為什么是對的?如果還是不明白這個算法為什么是對的,再繼續往下看。
舉個例子,6 2 5 4 3 1,觀察一下,下一個數列是6 3 1 2 4 5,回憶一下觀察的過程,你是先從后往前找的對不對?你在找什么?
其實你在從后往前找,比較相鄰兩個數字,如果前一個數字比后一個數字大或相等,就繼續往前找,直到找到前一個數字比后一個數字小,我們將前一個數字的位置記為i,后一個數字的位置記為j,此時應該滿足s[i]<s[j]。這個例子中i = 1,j = 2。
可以看出從j開始的數列都是非升序排列的,因為剛剛在找的過程中,前一個數字都是比下一個數字大或相等。
要知道,非升序排列的數列不可能有“下一個”數列,即非升序排列的數列是“最大的”。那么從j開始的數列必定不存在“下一個”數列;另外一點,我們需要考慮i之前(不包括i)的數列嗎?比如例子中的6,不需要!因為從i開始的數列必定存在“下一個”數列,因為它不是非升序排列的,非升序排列的數列必定存在“下一個”數列。所以結論是,我們只考慮從從i開始的數列找“下一個”數列。
從j開始的數列不可能有“下一個”數列,那么“下一個”數列的i位置放的值不能是原數,而應該從后面的數找,找比原數大的,然后這個數要盡量小。那么如何找到這個最小的數呢?因為從j開始的數列是非升序,所以從后往前找,找到比原數大的就行了。這個位置,我們記為k,例子中這個最小的數是3,即k=4。
找到k之后,交換i,k位置的值(一定可以找到這樣的k,因為j位置就滿足了)。交換之后,從j開始的數列依然是非升序排列。這個很明顯,就不多解釋了。
因為從j開始的數列是非升序排列,逆序一下,得到非降序排列,非降序排列是“最小的”。所以交換之后,從j開始,逆序排序一下,即得到“下一個”排列。
下面自己實現的源代碼(原理同STL中的next_permutation)

bool nextPermutation(int s[], int first, int last) //last最后一個元素的下一個位置 { if (last-first <= 1) //保證至少2個元素 return false; int i = last-2, j = last-1; while (1) { if (s[i] < s[j]) //只要存在這樣的對,下一個排列就必定存在 { int k = last; while (s[i] >= s[--k]); swap(s[i], s[k]); reverse(s+j, s+last); return true; } if (i == first) //找到第一個元素都找不到,說明該數組是降序排列的,下一個排列不存在 { reverse(s+first, s+last); return false; } i--, j--; } }
ps.理解源代碼之后就可以知道為什么降序排列的數組,用STL中的next_permutation之后變成升序排列
最后再補充一個不規范的遞歸版本,所謂不規范,就是排列的順序與STL中的next_permutation不一樣。

void nextPermutationRecursive(int s[], int n, int first, int last) //last是最后一個元素的下一個位置 { if (last-first == 1) //只含一個元素 { for (int i = 0; i < n; ++i) cout << s[i]; cout << endl; } else { for (int i = first; i < last; ++i) { swap(s[first], s[i]); nextPermutationRecursive(s, n, first+1, last); swap(s[first], s[i]); } } }