C++ 排列计算中唯一性的困惑
将下面的问题作为一个算法难题来解决。参考了一些类似的解决方案(并在下面发布其中的一个),尝试了一下,效果很好。问题是,对于“swap(num[i],num[k]);”行,我们如何确保始终可以交换到以前从未尝试过的数字(例如,假设我们在for循环的当前迭代中用2交换1,那么我们有可能在递归调用的相同级别/层的相同for循环的下一次迭代中用1交换2)?我有点困惑,因为我们通过引用传递num,很可能稍后(较低级别/层)递归调用会修改num的内容,这会导致我们已经计算过的数字调回。然而,我尝试了,它适用于我所有的测试用例。想知道下面的解决方案是否100%正确,或者是否碰巧通过了我的测试用例?:) 下面是我正在调试的详细问题说明和代码 给定可能包含重复项的数字集合,返回所有可能的唯一排列 比如说,, [1,1,2]具有以下独特排列: [1,1,2]、[1,2,1]和[2,1,1]C++ 排列计算中唯一性的困惑,c++,algorithm,C++,Algorithm,将下面的问题作为一个算法难题来解决。参考了一些类似的解决方案(并在下面发布其中的一个),尝试了一下,效果很好。问题是,对于“swap(num[i],num[k]);”行,我们如何确保始终可以交换到以前从未尝试过的数字(例如,假设我们在for循环的当前迭代中用2交换1,那么我们有可能在递归调用的相同级别/层的相同for循环的下一次迭代中用1交换2)?我有点困惑,因为我们通过引用传递num,很可能稍后(较低级别/层)递归调用会修改num的内容,这会导致我们已经计算过的数字调回。然而,我尝试了,它适用
class Solution {
public:
void recursion(vector<int> num, int i, int j, vector<vector<int> > &res) {
if (i == j-1) {
res.push_back(num);
return;
}
for (int k = i; k < j; k++) {
if (i != k && num[i] == num[k]) continue;
swap(num[i], num[k]);
recursion(num, i+1, j, res);
}
}
vector<vector<int> > permuteUnique(vector<int> &num) {
sort(num.begin(), num.end());
vector<vector<int> >res;
recursion(num, 0, num.size(), res);
return res;
}
};
类解决方案{
公众:
无效递归(vector num、int i、int j、vector&res){
如果(i==j-1){
res.push_back(num);
返回;
}
对于(int k=i;k
提前感谢,,
Lin在下面,您可以找到一个用Java编写的解决方案。很抱歉,没有在C++中提供解决方案,我很久没有使用它了。但语法将是类似的 解决方案是使用回溯() 另外,我使用hashset检查唯一性,可能有一个解决方案不使用任何hashset类型的数据结构,因为我的解决方案使用额外的内存来提供唯一的解决方案 样本输入和输出
input : [1, 1, 2]
output : [1, 1, 2]
[1, 2, 1]
[2, 1, 1]
解决办法是,
public class permutation {
public static void main(String[] args) {
permutation p = new permutation();
p.permute(new int[] { 1, 1, 2 });
}
HashSet<String> set = new HashSet<String>();
private void permute(int[] arr) {
set.clear();
this.permute(arr, 0, arr.length - 1);
}
private void permute(int[] arr, int l, int r) {
if (l == r) {
String key = Arrays.toString(arr);
if (set.contains(key))
return;
set.add(key);
System.out.println(key);
} else {
for (int i = l; i <= r; i++) {
swap(arr, l, i);
permute(arr, l + 1, r);
swap(arr, i, l);
}
}
}
private void swap(int[] arr, int l, int r) {
int tmp = arr[l];
arr[l] = arr[r];
arr[r] = tmp;
}
}
公共类置换{
公共静态void main(字符串[]args){
置换p=新置换();
p、 置换(新的int[]{1,1,2});
}
HashSet=newhashset();
私有无效置换(int[]arr){
set.clear();
此.permute(arr,0,arr.length-1);
}
私有无效置换(int[]arr,int l,int r){
如果(l==r){
字符串键=Arrays.toString(arr);
if(设置包含(键))
返回;
set.add(键);
系统输出打印项次(键);
}否则{
对于(int i=l;i,正如@notmyfriend在评论中所说,num
实际上是在每个函数调用中复制的。
所以现在可以归结为:
- 在所有数组值中,选择一个作为第一个数组值并将其放置在那里。
每个值在循环中循环一次,然后递归:
- 在第一个值之后的所有值中,选择一个作为第一个值并将其放置在那里
…等等,再加上检查以过滤掉没有变化的掉期,即过滤掉重复项
如果num
是一个真正的参考,它将不再工作(至少没有额外的步骤)。
例1 2是一个简单的上下文示例,它将给出以下结果:
112, 121, 211, 112, 121
也就是说,尽管进行了检查,但仍存在重复项(并且可能存在重复项
是一些根本不生成置换的示例)
关于评论:
默认情况下,C++中的每个正常函数参数都复制
(正常=没有明确的参考符号“&”等)。
也许你在考虑C风格的数组:本质上,传递给它的是一个指针(指向第一个值)。指针被复制,但原始和复制的指针都指向同一个内存位置
虽然std::vector
的目的是(也)包含数组,但向量本身是单个类对象(其中包含指向某个位置的值的指针)。类可以定义自己应该如何复制(使用复制构造函数)。
从技术上讲,向量类可以实现复制作为指针复制,然后它将具有与传递整个向量作为参考的相同效果;但是C++创建者希望保持复制语义,即,复制容器类应该复制所有值的真实拷贝。
对于非复制,已经有引用了…num没有通过引用传递到recursion()
-每个调用都会获得一个全新的数字数组副本。在我看来,“if(i!=k&&num[i]==num[k])continue”行创建了大量重复检查,if(num[i]==num[k])更简单继续;这样更好,因为该循环中的第一次遍历不会进入递归调用,而且int j参数始终是num.size,因此您可能需要将其删除simplicity@user1316208没有i!=k
,第一个递归(没有任何更改/某些内容与自身交换)不会发生=>没有结果。i!=k
是必需的。@deviantfan在我看来,结果将在for循环中进一步获得。但可能是wrong@user1316208尝试使用12(手动,或代码,你喜欢的)。不会有任何结果(但可能我也错了:)除了错误的语言,OP已经有了一个有效的解决方案(也比你的好),只是不理解它。谢谢DeviceFan,如果它是按值复制的。那我就理解了。但是为什么它是按值复制的?我可能错了,但我认为如果我们传递一个向量if integer,它是按引用传递的?不?谢谢你提供更多的细节。@LinMa看到答案中的加法。谢谢DeviceFan对传递引用行为的澄清对于一个向量,我想知道当只为整型向量或向量传递一个对向量的引用时,是否制作一个真正的副本