BM56 有重复项数字的全排列
知识点递归
描述
给出一组可能包含重复项的数字,返回该组数字的所有排列。结果以字典序升序排列。
数据范围: ,数组中的值满足
要求:空间复杂度 ,时间复杂度
示例1
输入:
[1,1,2]
返回值:
[[1,1,2],[1,2,1],[2,1,1]]
示例2
输入:
[0,1]
返回值:
[[0,1],[1,0]]
暴力递归法
这个可以借鉴没有重复元素的全排列问题中的暴力递归解法,只需要在进行全排列求解的时候判断一下前一元素的值和当前是否相等,相等则跳过。
代码如下:
std::vector<std::vector<int>> perm_imp(std::vector<int> num)
{
std::vector<std::vector<int>> res;
if (num.size() == 0)
{
return res;
}
if (num.size() == 1)
{
res.push_back(num);
return res;
}
for (int i = 0; i < num.size(); ++i)
{
if (i > 0 && num[i] == num[i - 1])// 跳过上一个相等的元素
{
continue;
}
std::vector<int> cur = num;
cur.erase(cur.begin() + i);
auto cur_perm = perm_imp(cur);
for (auto &x : cur_perm)
{
x.push_back(num[i]);
res.push_back(x);
}
}
return res;
}
std::vector<std::vector<int>> permuteUnique(std::vector<int> &num)
{
std::sort(num.begin(), num.end());
auto ans = perm_imp(num);
std::sort(ans.begin(), ans.end(), [](std::vector<int> &a, std::vector<int> &b)
{
for (int i = 0; i < a.size();++i)
{
if (a[i] < b[i])
{
return true;
}
else if (a[i] > b[i])
{
return false;
}
}
return false; });
return ans;
}
回溯+记忆化的实现
只需要在没有重复元素的回溯+记忆化实现基础上增加一格内层的判断——如果当前元素和前一元素相等,且前一元素已经被访问过,则跳过当前元素。
void perm_imp(std::vector<std::vector<int>> &res, std::vector<int> &num, std::vector<int> &tmp, std::vector<int> &visited)
{
if (tmp.size() == num.size())
{
res.push_back(tmp);
return;
}
for (int i = 0; i < num.size(); ++i)
{
if (visited[i] == 1)
{
continue;
}
if (i > 0 && num[i] == num[i - 1] && visited[i - 1] == 1)
{
continue;
}
visited[i] = 1;
tmp.push_back(num[i]);
perm_imp(res, num, tmp, visited);
tmp.pop_back();
visited[i] = 0;
}
}
std::vector<std::vector<int>> permuteUnique(std::vector<int> &num)
{
std::sort(num.begin(), num.end());
std::vector<int> visited(num.size(), 0);
std::vector<std::vector<int>> res;
std::vector<int> tmp;
perm_imp(res, num, tmp, visited);
return res;
}
关于回溯+交换算法为何不可以解决有重复元素的全排列问题
假设我们使用回溯+交换的方法写代码,应该是下面这样子:
void perm_imp(std::vector<std::vector<int>> &res, std::vector<int> &num, int index)
{
if (index == num.size() - 1)
{
res.push_back(num);
return;
}
for (int i = index; i < num.size(); ++i)
{
if (index != i && (num[i] == num[index]))
{
continue;
}
std::swap(num[index], num[i]);
perm_imp(res, num, index + 1);
std::swap(num[index], num[i]);
}
}
std::vector<std::vector<int>> permuteUnique(std::vector<int> &num)
{
std::sort(num.begin(), num.end());
std::vector<int> visited(num.size(), 0);
std::vector<std::vector<int>> res;
perm_imp(res, num, 0);
return res;
}
如果输入为[0,3,3,3],当index = 0的时候,将会生成4个序列,分别为[0,3,3,3][3,0,3,3][3,3,0,3][3,3,3,0]。对于[3,0,3,3][3,3,0,3]序列他们进入递归的时候index分别为1和2。索引为1的序列在下一次进入递归的时候会再次生成序列3。这样会造成同一个序列被生成多次。问题的根源在于由于我们在进入递归前交换了元素的位置,导致元素乱序,当我们进入索引为index的递归的时候,可能有些元素本身在index后面的被交换到了它的前面,而我们又无法判断前面元素的状态,因此无法解决该问题。