LeetCode 47 Permutations II

453 阅读4分钟

LeetCode 47 Permutations II

思路

这题与Permutations的不同在于,此题有重复的数字。因此,如果跳过重复的数字是本题的关键点之一。

  1. 第一种解法与Permutations的解法类似,只不过跳过了与当前元素相同的元素。仅仅交换本身、和当前元素不同的元素。每次循环,将不同的元素交换到cur位置。比如[1,1,2,2,3,3,3,4],如果cur为0,则第一次循环,将第一个1与其本身交换;第二次循环,将第一个2与cur指向的1交换,数组变为[2,1,1,2,3,3,3,4];第三次循环,将第一个3与cur指向的2交换,数组变为[3,1,1,2,2,3,3,4];第四次循环,将4与cur指向的3交换,数组变为[4,1,1,2,2,3,3,3]。此方法的思路是:每次循环,在cur指向的位置,安排一个新值。第一层函数给cur为0的位置安排新值,第二层函数给cur为1的位置安排新值,依次类推。正是因为采用这种交换方式,递归函数传参不必使用传引用。结束条件是cur == nums.size() - 1。将结束条件设置为cur == nums.size()也可,只不过是多套了一层函数,对结果不影响,因为最后一个元素只能与其本身交换。

  2. 第二种解法通过map<int, int>记录不同值对应的个数。key为值,value为个数。比如[1,1,2,2,3,3,3,4],1有2个,2有2个,3有3个,4有1个。在循环中,判断当前选定的元素是否可以加入permutation中,如果数量为0,表示已经用完了,不能加入。这种解法用函数的件套层数来对不同的位置取值。第一层函数就是尝试在permutation的第一个位置按照map中key的顺序取值,第二层函数就是在第二个位置按照map的位置取值,依次类推。在例子中,第一层函数先在第一个位置尝试1,所有以1开始的permutaion都尝试完后,然后在第一个位置尝试2等等。

  3. 第三种解法是第二种解法的变形,用一个visited数组记录当前元素是否被加入数组,至于递归的原理,则一摸一样。第二种方法中,是对map的遍历,因此,每次循环的key都不同,但是在第三种方法中,由于是对nums的遍历,并不能保证下一次循环的key不同,因此,需要加一个内层循环,跳过相同的元素。内层循环一定要放在if条件内。在if条件内,表示跳过与已经尝试过的nums[i]相同的元素,如果放在if条件外,不论visited[i]的值,都要跳过相同的元素。比如[1,1,2,2,3,3,3,4]中,在第一层函数将第一个1放入permutation后,如果内部循环在if条件外,则,第二层函数将会判断完visited[0]为1后,将第二个1跳过,因此,第二个1永远不会加入到permutation中,则permutaion的size永远不会等于nums的size,最后的结果将是一个空集。

代码

第一种解法

class Solution {
public:
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        if (nums.empty()) return {};
        
        sort(nums.begin(), nums.end());
        vector<vector<int>> rs;
        
        
        dfs(rs, nums, 0);
        
        return rs;
    }
    
    void dfs (vector<vector<int>> &rs, vector<int> nums, int cur) {
        if (cur == nums.size() - 1) {
            rs.push_back(nums);
            return ;
        }
        
        for (int i = cur; i < nums.size(); ++i) {
            if (i != cur && nums[i] == nums[cur]) continue;
            swap(nums[cur], nums[i]);
            dfs(rs, nums, cur+1);
        }
    }
}; 

第二种解法

class Solution {
public:
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        if (nums.empty()) return {};
        
        vector<vector<int>> rs;
        vector<int> permutation;
        map<int, int> mp;
        for (const auto &num : nums) 
            ++mp[num];
        
        dfs(rs, nums, permutation, mp);
        
        return rs;
    }
    
    void dfs (vector<vector<int>> &rs, vector<int> &nums, vector<int> &permutation, map<int, int> &mp) {
        if (permutation.size() == nums.size()) {
            rs.push_back(permutation);
            return ;
        }
        
        for (auto &p : mp) {
            if(p.second) {
                --(p.second);
                permutation.push_back(p.first);
                dfs(rs, nums, permutation, mp);
                permutation.pop_back();
                ++(p.second);
            }
        }
    }
};

第三种解法

class Solution {
public:
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        if (nums.empty()) return {};
        
        sort(nums.begin(), nums.end());
        vector<int> visited(nums.size(), 0);
        vector<vector<int>> rs;
        vector<int> permutation;
        
        dfs(rs, nums, permutation, visited);
        
        return rs;
    }
    
    void dfs(vector<vector<int>> &rs, vector<int> &nums, vector<int> &permutation, vector<int> &visited) {
        if (permutation.size() == nums.size()) {
            rs.push_back(permutation);
            return ;
        }
        
        for (int i = 0; i < nums.size(); ++i) {
            if (!visited[i]) {
                visited[i] = 1;
                permutation.push_back(nums[i]);
                dfs(rs, nums, permutation, visited);
                permutation.pop_back();
                visited[i] = 0;
                while (i + 1 < nums.size() && nums[i+1] == nums[i]) ++i;
            }
        }
    }
};