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].
本题要求输入一数组,输出数组的所有排列方式。题目中给定的为int类型的数组,有些题目中给定的为字符类型的,两种思路是一样的。在LeetCode中有两道关于Permutation的题目,第一题给定的数组不存在duplicate,也就是没有重复元素,第二题中存在重复元素。
对于排列可以通过交换元素得到。例如,输入为[1,2,3]可以由以下思路得到:
也就是说可以通过从后往前交换元素得到新的排列,但是在对交换后的排列进行交换的时候会打乱原来的顺序,因此可能得到重复的排列方式。所以可以再交换后恢复到原始组合,再对原始组合进行交换,避免重复的发生。从下面这张图上可以参考排列的思路:
代码如下:
class Solution {
public:
void Perm(vector<int>& num, int i, vector<vector<int> > &r)
{
if (i == num.size())
{
r.push_back(num);
}
for (int k = i; k != num.size(); ++k)
{
swap(num[k], num[i]);
Perm(num, i + 1, r);
swap(num[k], num[i]);
}
}
vector<vector<int> > permute(vector<int> &num) {
if (!num.size())
{
vector<vector<int> > r;
return r;
}
vector<vector<int> > r;
Perm(num, 0, r);
return r;
}
};
从上述代码中可以看出,递归的调用了Perm函数得到新的排列,且排列的实际实现是从后往前交换得到的,在每次交换完之后恢复顺序避免了数组被打乱。
上面给出了递归的解决办法,这里给出非递归的方法。在STL中已经给出了实现排列的函数next_permutation得到下一个排列,循环调用函数,直到没有新的排列为止,代码如下:
class Solution {
public:
vector<vector<int> > permuteUnique(vector<int> &num) {
vector<vector<int> > permu;
sort(num.begin(), num.end());
permu.push_back(num);
while (next_permutation(num.begin(), num.end()))
{
permu.push_back(num);
}
return permu;
}
};
当然,直接调用STL函数其实也算是“作弊”的做法了,毕竟这里考的就是算法嘛。那既然STL存在这样的一个函数,我们很自然的想到,我们能否自己写一个类似的函数呢。答案当然是肯定的(O(∩_∩)O~~~),下面就来介绍一下next_permutation算法。
若输入数组为[3,2,1,4]
该算法不仅对非重复数组有用,对重复的是一样的,因为两个元素相等是为非顺序对,所以不会交换,也就不会出现重复的情况了。
根据上面的描述我们很容易写出下面的代码:
class Solution {
public:
void reverse(vector<int>& num, int i)
{
int j = num.size() - 1;
while (i < j)
{
swap(num[i++], num[j--]);
}
}
bool nextPerm(vector<int>& num)
{
int i = num.size() - 1;
int j;
while (i)
{
j = i;
i--;
if (num[i] < num[j])
{
int k = num.size() - 1;
while (num[k] <= num[i])
k--;
swap(num[i], num[k]);
reverse(num, j);
return true;
}
}
reverse(num, i);
return false;
}
vector<vector<int> > permute(vector<int> &num) {
if (!num.size())
{
vector<vector<int> > r;
return r;
}
vector<vector<int> > r;
sort(num.begin(), num.end());
int count(0);
do
{
r.push_back(num);
} while (nextPerm(num));
return r;
}
};
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].
在使用上面的递归解法的时候是无法处理数组存在重复数值的情况的。例如对题目描述中的例子,如果存在重复数字,则递归调用仍然会得到六种排序,其中有三种重复。因此,在递归调用中我们有以下两种思路:
1、 使用sort排序,然后排除相邻的重复数字。注意没排序的不可以这么判断
2 、利用一个新的容器,装已经处理的数字,不重复处理数字,利用map和set等都可以。
第一种解法代码如下所示:
vector<int> mediRes;
vector<vector<int> > res;
vector<vector<int> > permuteUnique(vector<int> &num) {
sort(num.begin(), num.end());
permuII(num);
return res;
}
void permuII(vector<int> &num)
{
int m = num.size();
if (m == 1)
{
mediRes.push_back(num[0]);
res.push_back(mediRes);
mediRes.pop_back();
return;
}
//如果已经排序了的就可以值处理相邻的重复了
for (int i = 0; i < m; i++)
{
while (i < m-1 && num[i] == num[i+1])
i++;
mediRes.push_back(num[i]);
vector<int> cur = num;
cur.erase(cur.begin()+i);
permuII2(cur);
mediRes.pop_back();//注意:别忘记了这里需要弹出,不要等到循环结束
}
}
思路二的解法为:
vector<vector<int> > res;
vector<int> mediRes;
vector<vector<int> > permuteUnique(vector<int> &num) {
permuII(num);
return res;
}
void permuII(vector<int> &num)
{
int m = num.size();
if (m == 1)
{
mediRes.push_back(num[0]);
res.push_back(mediRes);
mediRes.pop_back();
return;
}
unordered_set<int> used;
for (int i = 0; i < m; i++)
{
if (used.find(num[i]) == used.end())
{
mediRes.push_back(num[i]);
vector<int> cur = num;
cur.erase(cur.begin()+i);
permuII(cur);
mediRes.pop_back();
used.insert(num[i]);
}
}
}
非递归解法可以参考第一题的方法。
LeetCode (18) Permutations I & II (排列一、二)
原文地址:http://blog.csdn.net/angelazy/article/details/45195187