您的位置:首页 > 其它

Permutations 全排列生成算法

2016-08-04 19:10 543 查看
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

反转 p[j] 后面的数得到 p[1] … p[i-2] p[j] p
… 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
;
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--;
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: