LeetCode:Permutations, Permutations II(求全排列)


Permutations

Given a collection of numbers, return all possible permutations.

For example,
[1,2,3] have the following permutations:
[1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], and [3,2,1].

求數組元素的全排列,數組不含重復元素

 

算法1:遞歸

類似於DFS的遞歸. 對於包含n個元素的數組,先確定第一位置的元素,第一個位置有n中可能(每次把后面的元素和第一個元素交換),然后求子數組[2…n]的全排列。由於一個數列的總共有n!個排列,因此時間復雜度為O(n!)

class Solution {
public:
    vector<vector<int> > permute(vector<int> &num) {
        vector<vector<int> > res;
        if(num.size() == 0)return res;
        vector<int> tmpres;
        permuteRecur(num, 0, res, tmpres);
        return res;
    }
    
    void permuteRecur(vector<int> &num, int index, vector<vector<int> >&res, vector<int>&tmpres)
    {
        if(index == num.size())
        {
            res.push_back(tmpres);
            return;
        }
        for(int i = index; i < num.size(); i++)
            {
                swap(num[index], num[i]);
                tmpres.push_back(num[index]);
                permuteRecur(num, index+1, res, tmpres);
                tmpres.pop_back();
                swap(num[index], num[i]);
            }
    }
};

Permutations II

Given a collection of numbers that might contain duplicates, return all possible unique permutations.

For example,
[1,1,2] have the following unique permutations:
[1,1,2], [1,2,1], and [2,1,1].

求數組元素的全排列,數組含重復元素

分析:要避免重復,就要保證我們枚舉某個位置的元素時,不能枚舉重復。

算法2:

在算法1的基礎上,當我們枚舉第i個位置的元素時,若要把后面第j個元素和i交換,則先要保證[i…j-1]范圍內沒有和位置j相同的元素。有以下兩種做法(1)可以每次在需要交換時進行順序查找;(2)用哈希表來查重。具體見下面的代碼。

注意不要誤以為以下兩種做法能夠去重:(1)最開始先對數組進行排序,以后每次交換時,只要保證當前要交換的元素和前一個元素不同,這種做法是錯誤的,雖然開始進行了排序,但是元素的交換會使數組再次變的無序(2)每次進入遞歸函數permuteRecur時,對從當前索引開始的子數組排序,這種做法也是錯誤的,因為每次交換元素后,我們要恢復交換的元素,如果在遞歸函數內排序,就不能正確的恢復交換的元素。                                   本文地址

class Solution {
public:
    vector<vector<int> > permuteUnique(vector<int> &num) {
        vector<vector<int> > res;
        if(num.size() == 0)return res;
        vector<int> tmpres;
        permuteRecur(num, 0, res, tmpres);
        return res;
    }
    
    void permuteRecur(vector<int> &num, int index, vector<vector<int> >&res, vector<int>&tmpres)
    {
        if(index == num.size())
        {
            res.push_back(tmpres);
            return;
        }
        for(int i = index; i < num.size(); i++)
            if(i == index || !find(num, index, i, num[i]))
            {
                swap(num[index], num[i]);
                tmpres.push_back(num[index]);
                permuteRecur(num, index+1, res, tmpres);
                tmpres.pop_back();
                swap(num[index], num[i]);
            }
    }
    //從數組的[start,end)范圍內尋找元素target
    bool find(vector<int> &num, int start, int end, int target)
    {
        for(int i = start; i < end; i++)
            if(num[i] == target)
                return true;
        return false;
    }
};

 

class Solution {
public:
    vector<vector<int> > permuteUnique(vector<int> &num) {
        vector<vector<int> > res;
        if(num.size() == 0)return res;
        vector<int> tmpres;
        permuteRecur(num, 0, res, tmpres);
        return res;
    }
    
    void permuteRecur(vector<int> &num, int index, vector<vector<int> >&res, vector<int>&tmpres)
    {
        if(index == num.size())
        {
            res.push_back(tmpres);
            return;
        }
        unordered_set<int> umap;
        for(int i = index; i < num.size(); i++)
            if(umap.find(num[i]) == umap.end())
            {
                umap.insert(num[i]);
                swap(num[index], num[i]);
                tmpres.push_back(num[index]);
                permuteRecur(num, index+1, res, tmpres);
                tmpres.pop_back();
                swap(num[index], num[i]);
            }
    }
};

 

算法3:

我們知道c++STL中有個函數next_permutation,這個函數時求某個排列的下一個大的排列。所謂的下一個大的排列可以如下解釋:如果把數組元素看成是某個字符,這些字符組成一個字符串,下一個大的排列就是比當前排列代表的字符串更大(按字典序比較),且不存在介於兩個字符串之間的字符串。例如對於字符串abc,它的下一個大排列是acb。

對於某個排列,我們如下求它的下一個大的排列:

  • 從最尾端開始往前尋找兩個相鄰的元素,兩者滿足i < ii(令第一個元素為i,第二個元素為ii)
  • 如果沒有找到這樣的一對元素則,表明當前的排列是最大的,沒有下一個大的排列
  • 如果找到,再從末尾開始找出第一個大於i的元素,記為j
  • 交換元素i, j,再將ii后面的所有元素顛倒排列(包括ii)
  • 按照的STL實現,如果某個排列沒有比他大的下一個排列,調用這個函數還是會把原排列翻轉,即得到最小的排列

有了這個函數后,這一題,我們先對數組按照升序排序,這樣初始排列就是最小的,然后循環對數組求next_permutation,直到找不到下一個大的排列。

class Solution {
public:
    vector<vector<int> > permuteUnique(vector<int> &num) {
        vector<vector<int> > res;
        if(num.size() == 0)return res;
        sort(num.begin(), num.end());
        res.push_back(num);
        while(mynext_permutation(num))res.push_back(num);
        return res;
    }
    
    bool mynext_permutation(vector<int>&num)
    {
        int n = num.size();
       if(n <= 1)return false;
       for(int i = n-2, ii = n-1; i >= 0; i--,ii--)
       {
           if(num[i] < num[ii])
           {
               int j = n-1;
               while(num[j] <= num[i])j--;//從尾部找到第一個比num[i]大的數,一定可以找到
               swap(num[i], num[j]);
               reverse(num.begin()+ii, num.end());
               return true;
           }
       }
       reverse(num.begin(), num.end());
       return false;
    }
};

 

STL還有一個prev_permutation函數,求某個排列的上一個比他小的排列,方法和next_permutation相似:

對於某個排列,我們如下求它的上一個更小的排列:

  • 從最尾端開始往前尋找兩個相鄰的元素,兩者滿足i > ii(令第一個元素為i,第二個元素為ii)
  • 如果沒有找到這樣的一對元素則,表明當前的排列是最小的,沒有下一個更小的排列
  • 如果找到,再從末尾開始找出第一個小於i的元素,記為j
  • 交換元素i, j,再將ii后面的所有元素顛倒排列(包括ii)
  • 按照的STL實現,如果某個排列沒有比他小的下一個排列,調用這個函數還是會把原排列翻轉,即得到最大的排列

有了這個函數后,這一題,我們先對數組按照降序排序,這樣初始排列就是最大的,然后循環對數組求prev_permutation,直到找不到下一個更小的排列。

class Solution {
public:
    vector<vector<int> > permuteUnique(vector<int> &num) {
        vector<vector<int> > res;
        if(num.size() == 0)return res;
        sort(num.begin(), num.end(), greater<int>());
        res.push_back(num);
        while(myprev_permutation(num))res.push_back(num);
        return res;
    }
    
    bool myprev_permutation(vector<int>&num)
    {
        int n = num.size();
       if(n <= 1)return false;
       for(int i = n-2, ii = n-1; i >= 0; i--,ii--)
       {
           if(num[i] > num[ii])
           {
               int j = n-1;
               while(num[j] >= num[i])j--;//從尾部找到第一個比num[i]小的數,一定可以找到
               swap(num[i], num[j]);
               reverse(num.begin()+ii, num.end());
               return true;
           }
       }
       reverse(num.begin(), num.end());
       return false;
    }
};

 

 

【版權聲明】轉載請注明出處:http://www.cnblogs.com/TenosDoIt/p/3662644.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM