1、使用遞歸生成算法
/*
* 遞歸輸出序列的全排列
*/
void permutation(char* array, size_t array_size, unsigned int index)
{
if(index >= array_size)
{
for(unsigned int i = 0; i < array_size; ++i)
{
cout << array[i] << ' ';
}
cout << '\n';
return;
}
for(unsigned int i = index; i < array_size; ++i)
{
swap(array, i, index);
<span style="font-family: Arial, Helvetica, sans-serif;">permutation</span>(array, array_size, index + 1);
swap(array, i, index); //相當於回溯
}
}
遞歸算法總是那麼容易的理解,一般的問題是沒有問題的,但是有個缺點,就是當序列中有重複的元素,上面的遞歸需要修改才能得到全排列
2、使用字典序算法
所謂字典序算法,就是根據當前字典序查找下一個m大於當前字典序並且不存在一個大於當前序列小於m的序列,即大於當前序列的最小值序列
字典序排序生成算法
字典序法就是按照字典排序的思想逐一產生所有排列。
例如,由1,2,3,4組成的所有排列,從小到大的依次爲:
1234, 1243, 1324, 1342, 1423, 1432,
2134, 2143, 2314, 2341, 2413, 2431,
3124, 3142, 3214, 3241, 3412, 3421,
4123, 4132, 4213, 4231, 4312, 4321.
分析這種過程,看後一個排列與前一個排列之間有什麼關係?
再如,設有排列(p)=2763541,按照字典式排序,它的下一個排列是什麼?
- 2763541 (找最後一個正序35)
- 2763541 (找3後面比3大的最後一個數4)
- 2764531 (交換3,4的位置)
- 2764135 (把4後面的5,3,1反轉)
下面給出求 p[1…n] 的下一個排列的描述:
- 求 i = max{j | p[j – 1] < p[j]} (找最後一個正序)
- 求 j = max{k| p[i – 1] < p[k]} (找最後大於 p[i – 1] 的)
- 交換 p[i – 1] 與 p[j]得到 p[1] … p[i-2] p[j] p[i] p[i+1] … p[j-1] p[i-1] p[j+1] … p[n]
- 反轉 p[j] 後面的數得到 p[1] … p[i-2] p[j] p[n] … p[j+1] p[i-1] p[j-1] … p[i]
Next Permutation
Description:
- Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers.
- If such arrangement is not possible, it must rearrange it as the lowest possible order (ie, sorted in ascending order).
- The replacement must be in-place, do not allocate extra memory.
Here are some examples. Inputs are in the left-hand column and its corresponding outputs are in the right-hand column.
1,2,3 → 1,3,2
3,2,1 → 1,2,3
1,1,5 → 1,5,1
題意:實現一個求下一個排列的方法。要求:1) 下一個排列要在字典序上大於當前排列;2) 如果沒有比當前序列更大的排列,那麼下一個爲最小的排列;3) 需要在原地操作,即不用額外的空間。
分析:按照上面的方法直接實現即可。需要注意與上面方法不同的是,下面的實現是循環的,即字典序的最後一個排列[4,3,2,1]的後面是[1,2,3,4]。
public class Solution {
public void nextPermutation(int[] num) {
// find the last adjacent two element that is in ascending order
int i = num.length - 1;
while (i > 0 && num[i - 1] >= num[i]) {
i--;
}
// if the sequence is already in descending order, reverse the whole sequence
if (i == 0) {
reverse(num, 0, num.length - 1);
return;
}
// find the last element that is larger than num[i-1]
int j = num.length - 1;
while (j >= i && num[i - 1] >= num[j]) {
j--;
}
// exchange num[i-1] and num[j]
int tmp = num[i - 1];
num[i - 1] = num[j];
num[j] = tmp;
// reverse the sequence after i-1
reverse(num, i, num.length - 1);
}
public void reverse(int[] num, int start, int end) {
int l = start;
int r = end;
while (l < r) {
int tmp = num[l];
num[l] = num[r];
num[r] = tmp;
l++;
r--;
}
}
}
Permutations
Description: Given a collection of numbers, return all possible permutations. For example, [1,2,3] have the following permutations: [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], and [3,2,1].
題意:求集合中所有數的全排列。
分析:我們知道n個數的全排列有n!個,那麼根據 Next Permutation 一個一個求出即可。
<pre name="code" class="java" style="font-size: 16px; line-height: 28.8px;">public class Solution {
public List<List<Integer>> permute(int[] num) {
List<List<Integer>> ans = new ArrayList<List<Integer>>();
int i = 0;
do {
// turn every permutation to a list and add it to the answer
List<Integer> list = new ArrayList<Integer>();
for (int x : num) {
list.add(x);
}
ans.add(list);
// get next permutation
nextPermutation(num);
} while (++i < factorial(num.length));
return ans;
}
public void nextPermutation(int[] num) {
// find the last adjacent two element that is in ascending order
int i = num.length - 1;
while (i > 0 && num[i - 1] >= num[i]) {
i--;
}
// if the sequence is already in descending order, reverse the whole sequence
if (i == 0) {
reverse(num, 0, num.length - 1);
return;
}
// find the last element that is larger than num[i-1]
int j = num.length - 1;
while (j >= i && num[i - 1] >= num[j]) {
j--;
}
// exchange num[i-1] and num[j]
int tmp = num[i - 1];
num[i - 1] = num[j];
num[j] = tmp;
// reverse the sequence after i-1
reverse(num, i, num.length - 1);
}
public void reverse(int[] num, int start, int end) {
int l = start;
int r = end;
while (l < r) {
int tmp = num[l];
num[l] = num[r];
num[r] = tmp;
l++;
r--;
}
}
public int factorial(int n) {
return (n == 1 || n == 0) ? 1 : factorial(n - 1) * n;
}
}
Permutations II
Description: Given a collection of numbers that might contain duplicates, return all possible unique permutations. For example, [1,1,2] have the following unique permutations: [1,1,2], [1,2,1], and [2,1,1].
題意:求集合中所有數的全排列,注意集合中可能存在重複的數。
分析:方法與 Permutations 相同,只是有了重複數後,全排列的總數就不足n!個了,我們的方法是先對所有數排序,即從最小的排列開始找,找到最後一個排列時結束。
public class Solution {
public List<List<Integer>> permuteUnique(int[] num) {
List<List<Integer>> ans = new ArrayList<List<Integer>>();
// make the permutation strat from ascending order
Arrays.sort(num);
do {
// turn every permutation to a list and add it to the answer
List<Integer> list = new ArrayList<Integer>();
for (int x : num) {
list.add(x);
}
ans.add(list);
} while (nextPermutation(num));
return ans;
}
public boolean nextPermutation(int[] num) {
// find the last adjacent two element that is in ascending order
int i = num.length - 1;
while (i > 0 && num[i - 1] >= num[i]) {
i--;
}
// if all permutations have been found
if (i == 0) return false;
// find the last element that is larger than num[i-1]
int j = num.length - 1;
while (j >= i && num[i - 1] >= num[j]) {
j--;
}
// exchange num[i-1] and num[j]
int tmp = num[i - 1];
num[i - 1] = num[j];
num[j] = tmp;
// reverse the sequence after i-1
reverse(num, i, num.length - 1);
return true;
}
public void reverse(int[] num, int start, int end) {
int l = start;
int r = end;
while (l < r) {
int tmp = num[l];
num[l] = num[r];
num[r] = tmp;
l++;
r--;
}
}
}
Permutation Sequence
Description: The set [1,2,3,…,n] contains a total of n! unique permutations. By listing and labeling all of the permutations in order, We get the following sequence (ie, for n = 3):
1. “123”
2. “132”
3. “213”
4. “231”
5. “312”
6. “321”
Given n and k, return the kth permutation sequence. Note: Given n will be between 1 and 9 inclusive.
題意:按照字典序法,求n個數的全排列中第k個排列,注意返回排列的形式爲字符串。
分析:從最小的排列開始,根據 Next Permutation 找到第k個排列即可。
public class Solution {
public String getPermutation(int n, int k) {
// construct the first sequence
char[] seq = new char[n];
for (int i = 0; i < n; i++) {
seq[i] = (char) ('0' + i + 1);
}
// get the k-th permutation
for (int i = 1; i < k; i++) {
nextPermutation(seq);
}
return String.valueOf(seq);
}
public void nextPermutation(char[] seq) {
// find the last adjacent two element that is in ascending order
int i = seq.length - 1;
while (i > 0 && seq[i - 1] >= seq[i]) {
i--;
}
// if all permutations have been found
if (i == 0) return;
// find the last element that is larger than seq[i-1]
int j = seq.length - 1;
while (j >= i && seq[i - 1] >= seq[j]) {
j--;
}
// exchange seq[i-1] and seq[j]
char tmp = seq[i - 1];
seq[i - 1] = seq[j];
seq[j] = tmp;
// reverse the sequence after i-1
int l = i;
int r = seq.length - 1;
while (l < r) {
tmp = seq[l];
seq[l] = seq[r];
seq[r] = tmp;
l++;
r--;
}
}
}