1. 程式人生 > >每天一道LeetCode-----獲取無重複項/有重複項序列的全排列

每天一道LeetCode-----獲取無重複項/有重複項序列的全排列

原題連結Permutations
這裡寫圖片描述
要求是輸出給定序列的全排列,序列中不包含重複元素
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
這裡寫圖片描述
和上面要求一樣,唯一區別是給定的序列可能有重複的元素
如果再按照上面的方法,那麼會出現大量重複的結果,比如第一個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]);
        }
    }
};