leetcode46.Permutation & leetcode47.Permutation II
问题描述:
Given a collection of distinct integers, return all possible permutations.
Example:
Input: [1,2,3] Output: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ]
思路:
本题中给出的数组中不包含重复数组,所以相对简单。
可以使用DFS来寻找,同时使用visited数组进行辅助。
代码:
class Solution { public: void permuteDFS(vector<int> &pm, vector<int> &visited, vector<vector<int>> &res, vector<int> &num){ if(pm.size() == num.size()){ res.push_back(pm); return; } for(int i = 0; i < num.size(); i++){ if(visited[i]) continue; visited[i] = 1; pm.push_back(num[i]); permuteDFS(pm, visited, res, num); pm.pop_back(); visited[i] = 0; } } vector<vector<int>> permute(vector<int>& nums) { vector<vector<int>> ret; vector<int> pm; vector<int> visited(nums.size(), 0); for(int i = 0; i < nums.size(); i++){ visited[i] = 1; pm.push_back(nums[i]); permuteDFS(pm, visited, ret, nums); pm.pop_back(); visited[i] = 0; } return ret; } };
另一种思路:
在别的大神那里看到了一种用swap的很巧妙的方法
假设nums为{1, 2, 3}
从第一个位置开始,我们找第一个位置是1的所有可能, 第一个位置是二的所有可能,第一个位置是三的所有可能
当第一个位置确定之后,我们可以去找第二个位置确定的所有可能。
依次递归。
代码:
class Solution { public: void findPermute(vector<int>& nums, vector<vector<int>> &ret, int start){ if(start == nums.size()){ ret.push_back(nums); return; } for(int i = start; i< nums.size(); i++){ swap(nums[start],nums[i]); findPermute(nums, ret, start+1); swap(nums[start],nums[i]); } } vector<vector<int>> permute(vector<int>& nums) { vector<vector<int>> ret; findPermute(nums, ret, 0); return ret; } };
permutation II:
问题描述:
Given a collection of numbers that might contain duplicates, return all possible unique permutations.
Example:
Input: [1,1,2] Output: [ [1,1,2], [1,2,1], [2,1,1] ]
思路:
与第一个排列组合不同的是,给定的数组中可能会出现重复的数字,也就是是说当我们根据下标进行排列组合时可能会出现重复的组合
一开始我选择了用set来存储来避免重复,但是发现运行效率太低(66ms),因为将重复的排列有重新计算了一遍,所以可以先排序然后剪枝。
代码:
用set:
class Solution { public: void permuteDFS(vector<int>& nums, set<vector<int>> &vSet, int start){ if(start == nums.size()){ vSet.insert(nums); return; } for(int i = start; i < nums.size(); i++){ swap(nums[i], nums[start]); permuteDFS(nums, vSet, start+1); swap(nums[i], nums[start]); } } vector<vector<int>> permuteUnique(vector<int>& nums) { set<vector<int>> pmSet; permuteDFS(nums, pmSet, 0); vector<vector<int>> ret(pmSet.begin(), pmSet.end()); return ret; } };
剪枝:
class Solution { public: void recursion(vector<int> nums, int i, vector<vector<int> > &res) { if (i == nums.size()-1) { res.push_back(nums); return; } for (int k = i; k < nums.size(); k++) { if (i != k && nums[i] == nums[k]) continue; swap(nums[i], nums[k]); recursion(nums, i+1, res); } } vector<vector<int>> permuteUnique(vector<int>& nums) { sort(nums.begin(),nums.end()); vector<vector<int>> res; recursion(nums,0,res); return res; } };
from:https://leetcode.com/problems/permutations-ii/discuss/18596/A-simple-C++-solution-in-only-20-lines
需要注意的是:这里的nums是传值而非引用,所以并没有交换回来
而我一开始使用引用并且进行交换,这个时候就会出现问题不能通过OJ
引用: 传值:
可以从上面的输出结果看出
当得到一个组合并存入ret中后,由于将数组重置称成初始数组,有重复的组合出现而且我们没有办法检测,因为我们的判断语句是判断当前位置是否跟交换位置的数值相同,跟相同的值不同的位置交换检查不出来。但是如果不交换回去的话,可能会出换回去了的情况从而出现重复。因为传引用实际上会在参数上直接更改。
当传值的时候
由于是传值所以每次都会拷贝出一份新的从而不影响递归上一次的值,这样我们的检测语句也可以成功避免交换时会出现重复的情况。