混淆了置换计算中的唯一性

confused on uniqueness in permutation calculation

本文关键字:唯一性 计算      更新时间:2023-10-16

将以下问题作为一个算法难题进行处理。参考了一些类似的解决方案(并在下面发布了其中一个),尝试了一下,结果奏效了。问题是,对于"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]

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;
    }
};

提前感谢,Lin

正如@notmyfriend在评论中所说,num实际上是在每个函数调用中复制的
所以现在可以归结为:

  • 在所有数组值中,选择一个作为第一个值,并将其放置在那里
    在循环中为每个值一次,然后递归:
    • 在第一个值之后的所有值中,选择一个作为第一个值并将其放置在那里

等等,再加上一个检查,过滤掉没有任何变化的交换,即过滤掉重复项。

如果num是一个真正的参考,它将不再工作(至少没有额外的步骤)
1 12是一个简单的例子,它会给出以下结果:

112, 121, 211, 112, 121  

即。尽管进行了检查,但仍存在重复项(可能存在
也是根本不生成某些排列的示例)。

关于评论:

默认情况下,复制C++中的每个正常函数参数
(normal=没有明确的引用符号'&'等)。
也许你想到的是C风格的数组:本质上,传递给那里的是一个指针(指向第一个值)。指针被复制,但原始指针和复制指针都指向相同的内存位置。

虽然std::vector的目的(也是)包含一个数组,但向量本身是一个单独的类对象(它包含指向某个位置值的指针)。类可以定义自己应该如何复制(使用复制构造函数)
从技术上讲,向量类可以将复制实现为指针复制,那么它将具有与传递整个向量作为引用相同的效果;但是C++创建者希望保留复制语义,即复制容器类应该生成所有值都重复的真实副本
对于非复制,已经有引用。。。

下面可以找到用Java编写的解决方案。很抱歉没有在C++中提供解决方案,我已经很久没有使用它了。但语法会很相似。

解决方案使用回溯(https://en.wikipedia.org/wiki/Backtracking)此外,我使用哈希集来检查唯一性,可能有一种解决方案不使用任何哈希集类型的数据结构,因为我的解决方案使用额外的内存来提供唯一的解决方案。

样本输入和输出;

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;
    }
}