每天一道LeetCode-----获取无重复项/有重复项序列的全排列

原题链接Permutations
每天一道LeetCode-----获取无重复项/有重复项序列的全排列_第1张图片
要求是输出给定序列的全排列,序列中不包含重复元素
STL中有next_permutation函数可以获取当前序列的下一个排列,使用起来也很简单,先对序列按递增顺序排序,然后不断调用next_permutation函数获取当前序列的下一个更大的排列,如果没有更大的排列就返回false

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {

        std::sort(nums.begin(), nums.end());
        vector<vector<int>> res;
            res.emplace_back(nums);
        }while(next_permutation(nums.begin(), nums.end()));
        return res;

    }
};

这里主要是利用另一种方法实现,做个记录
对于全排列问题,又是没有重复项的序列,可以采用不断交换两个位置的方法获得所有的排列,即以某个点为开始点,依次和后面的做交换,因为没有重复项,所以得到的序列都是不同的结果,例如

1   2   3
/* 
 * 开始点为第一个数
 * 1和2交换 ->2    1   3 
 * 1和3交换 ->3    2   1
 */

2   1   3
/*
 * 从2   1   3递归
 * 开始点为第二个数
 * 1和3交换 ->2   3   1
 */

3   2   1
/*
 * 从3   2   1递归
 * 开始点为第二个数
 * 2和1交换 ->3   1   2
 */

/* 结果 */
1   2   3
2   1   3
2   3   1
3   1   2
3   2   1

但是这种方法少了一种结果,{1 3 2} 。因为序列{1 2 3}的时候是以1为开始点,但是没有对2和3进行交换。原因在于没有从{1 2 3}递归并且开始点是第二个数的过程。所以,解决办法就是当开始点进行交换时,也要和自己交换一次,但是交换的结果不能添加到结果中。但是这样对于第一个序列{1 2 3}就不在结果中,所以需要在交换前手动添加第一个序列。
代码如下

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        /* 从大到小排序 */
        std::sort(nums.begin(), nums.end());
        vector<vector<int>> res;
        /* 添加最初的序列 */
        res.push_back(nums);
        get_permutation(nums, 0, res);
        return res;
    }
private:
    void get_permutation(vector<int>& nums, int idx, vector<vector<int>>& res)
    {
        if(idx >= nums.size())
            return;
        /* 以idx位置作为开始点,依次和后面的交换 */
        for(int i = idx; i < nums.size(); ++i)
        {
            /* 交换,获取一个排列 */
            swap(nums[i], nums[idx]);
            /* 如果不是自己和自己交换,就把排列添加到结果中 */
            if(i != idx)
                res.push_back(nums);
            /* 递归调用,以下一个位置作为开始点 */
            get_permutation(nums, idx + 1, res);
            /* 交换完再换回来,回到开始的状态,然后和下一个交换 */
            swap(nums[i], nums[idx]);
        }
    }
};

扩展

Permutations II

原题链接Permutations II
每天一道LeetCode-----获取无重复项/有重复项序列的全排列_第2张图片
和上面要求一样,唯一区别是给定的序列可能有重复的元素
如果再按照上面的方法,那么会出现大量重复的结果,比如第一个1和第二个1交换,结果没有变换,但是被添加了两次。所以,在进行交换的时候,需要特别注意,以当前位置作为开始点和后面的元素进行交换时,这个元素在之前有没有交换过,如果交换过了,就不用再交换了。
例如,如果开始时序列为{1 2 7 7 9},某次交换以2为开始点

/* swap(nums[1], nums[2]) ->    1 7 2 7 9 */
开始点为第2个数22先和第一个7交换,变为1 7 2 7 9

/* swap(nums[2], nums[3]) ->    1 7 7 2 9 */
开始点变为第3个数7,7先和2交换,变为1 7 2 7 9

/* ... */

交换一轮后回到1 2 7 7 9
/* swap(nums[1], nums[3]) -> 1 7 7 2 9 */
开始点为第2个数2,2和第二个7交换,变为1 7 7 2 9
此时已经和先前的结果重复了

原因就在于如果有重复的元素,那么开始点第一次和重复元素的第一个元素交换,接着和第二个元素交换获取的排列,和开始点直接和重复元素的第二个元素交换获取的排列是一样的
所以,只需要和重复元素的第一个元素交换一次即可,后面重复的元素都不需要再次交换,因为在向后递归的过程中,开始点和后面的重复元素已经交换过了。

代码如下

class Solution {
public:
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        /* 递增排序 */
        std::sort(nums.begin(), nums.end());
        vector<vector<int>> res;
        /* 添加原始排列 */
        res.push_back(nums);
        get_permutation(nums, 0, res);
        return res;
    }
private:
    void get_permutation(vector<int>& nums, int idx, vector<vector<int>>& res)
    {
        if(idx >= nums.size())
            return;

        /* 记录是否有交换过 */
        std::unordered_set<int> hash;
        for(int i = idx; i < nums.size(); ++i)
        {
            if(hash.find(nums[i]) != hash.end())
                continue;
            hash.insert(nums[i]);
            swap(nums[i], nums[idx]);
            if(i != idx)
                res.push_back(nums);
            get_permutation(nums, idx + 1, res);
            swap(nums[i], nums[idx]);
        }
    }
};

你可能感兴趣的:(LeetCode)