代码题(19)— 组合与排列
1、77. 组合
给定两个整数 n 和 k,返回 1 ... n 中所有可能的 k 个数的组合。
示例:
输入: n = 4, k = 2 输出: [ [2,4], [3,4], [2,3], [1,2], [1,3], [1,4], ]
class Solution { public: vector<vector<int>> combine(int n, int k) { vector<vector<int>> res;
if(n < k || k<=0)
return res; vector<int> temp; combSum(n, k, 1, temp, res); return res; } void combSum(int n, int k, int pos, vector<int> &temp, vector<vector<int>> &res) { if(temp.size() == k) { res.push_back(temp); return; } for(int i=pos;i<=n;++i) { temp.push_back(i); combSum(n,k,i+1,temp,res); temp.pop_back(); } } };
2、从数组中取出n个元素的所有组合
思路如下:
首先我们找到这个字符组合中一定包含A字符的组合,那么A字符肯定就定下来了,即包含S中第一个字符的组合,然后以剩下的字符串BCDEF作为新的字符串,找出所有的2个字符的组合,那就得到了A包含的所有组合了是吧。。
然后我们就可以省去A了,因为包含A的所有的组合都找到了,所以我们又开始以BCDEF这个字符串找出所有的3个字符的组合;
那么做法和上面一样,又以第一个为指定的字符,CDEF进行找出所有2个字符的组合。这样包含B开头的所有字符组合也找完了吧‘
依次遍历下去,达到E的时候,不能再进行了,因为剩余长度小于3个了。
本质和上面是一样的/*我们有两种选择:第一是把这个字符放到组合中去,接下来我们需要在剩下的n-1个字符中选取m-1个字符;
第二是不把这个字符放到组合中去,接下来我们需要在剩下的n-1个字符中选择m个字符*/
void combineDfs(vector<int> &nums, int pos,int k, vector<int> &temp, vector<vector<int>> & res) { if (temp.size() == k) { res.push_back(temp); } else { for (int i = pos; i < nums.size(); ++i) //选择放还是不放 { temp.push_back(nums[i]); combineDfs(nums, i + 1, k, temp, res); temp.pop_back(); } } } vector<vector<int>> combine(vector<int> &nums, int k) { vector<int> temp; vector<vector<int>> res; combineDfs(nums, 0,k, temp, res); return res; } int main() { int n = 5, k = 3; vector<int> nums; //int a[] = { 2,4,6,8 }; for (int i = 1; i <= n; ++i) { nums.push_back(i); } vector<vector<int>> res; res = combine(nums, k); for (int i = 0; i < res.size(); ++i) { for (int j = 0; j < res[i].size(); ++j) { cout << res[i][j] << " "; } cout << endl; } system("pause"); return 0; }
3、46. 全排列
给定一个没有重复数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3] 输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ]
(1)这道题是求全排列问题,给的输入数组没有重复项,这跟之前的组合和类似,解法基本相同,但是不同点在于那道不同的数字顺序只算一种,是一道典型的组合题,而此题是求全排列问题,还是用递归DFS来求解。这里我们需要用到一个visited数组来标记某个数字是否访问过,然后在DFS递归函数从的循环应从头开始,而不是从level开始,这是和组合不同的地方,其余思路大体相同。
class Solution { public: vector<vector<int>> permute(vector<int>& nums) { vector<vector<int>> res; vector<int> temp; vector<int> visited(nums.size(),0); permuteDfs(nums,0,visited,temp,res); return res; } void permuteDfs(vector<int> &nums, int pos, vector<int> &visited, vector<int> &temp, vector<vector<int>> &res) { if(pos == nums.size()) res.push_back(temp); else { for(int i=0;i<nums.size();++i) { if(visited[i] == 0) { visited[i] = 1; temp.push_back(nums[i]); permuteDfs(nums,pos+1,visited,temp,res); temp.pop_back(); visited[i] = 0; } } } } };
(2)还有一种递归的写法,更简单一些,这里是每次交换num里面的两个数字,经过递归可以生成所有的排列情况。
class Solution { public: vector<vector<int>> permute(vector<int>& nums) { vector<vector<int>> res; permuteDfs(nums, 0, res); return res; } void permuteDfs(vector<int> &nums, int pos, vector<vector<int>> &res) { if(pos == nums.size()) res.push_back(nums); for(int i=pos;i<nums.size();++i) { swap(nums[pos], nums[i]); permuteDfs(nums, pos+1, res); swap(nums[pos], nums[i]); } } };
4、47. 全排列 II
还是DFS,不过有重复,那重点就是去重了。
同样深度的情况下,出现重复的,那么需要跳过。 具体说就是:
判断是否和上一个相等,相等的情况下如果上一个没用过,说明是上一个回溯结束的,同一层,那么就不要再重新来一轮了,跳过。 112 分别以1,1,2开始,第二个1,和第一个1开始的结果重复的。
在第一个1开始的时候,下一层的当前元素是第二个1,虽然也是1,但是上一个1被用了,说明它是不同深度的,所以不跳过。
这道题是之前那道 Permutations 全排列的延伸,由于输入数组有可能出现重复数字,如果按照之前的算法运算,会有重复排列产生,我们要避免重复的产生,在递归函数中要判断前面一个数和当前的数是否相等,如果相等,前面的数必须已经使用了,即对应的visited中的值为1,当前的数字才能使用,否则需要跳过,这样就不会产生重复排列了
class Solution { public: vector<vector<int>> permuteUnique(vector<int>& nums) { vector<vector<int>> res; vector<int> temp; vector<int> visited(nums.size(), 0); sort(nums.begin(), nums.end()); permute(nums, 0, visited, temp, res); return res; } void permute(vector<int> &nums, int pos, vector<int> &visited, vector<int> &temp, vector<vector<int>> &res) { if(temp.size() == nums.size()) { res.push_back(temp); } else { for(int i=0;i<nums.size();++i) { if(visited[i] == 0) { if(i>0 && nums[i] == nums[i-1] && visited[i-1] == 0) continue; visited[i] = 1; temp.push_back(nums[i]); permute(nums, i+1, visited, temp, res); temp.pop_back(); visited[i] = 0; } } } } };
还有一种比较简便的方法,在Permutations的基础上稍加修改,我们用set来保存结果,利用其不会有重复项的特点,然后我们再递归函数中的swap的地方,判断如果i和start不相同,但是nums[i]和nums[start]相同的情况下跳过,继续下一个循环.
class Solution { public: vector<vector<int>> permuteUnique(vector<int>& nums) { set<vector<int>> res; permuteDfs(nums, 0, res); return vector<vector<int>> (res.begin(), res.end()); } void permuteDfs(vector<int> &nums, int pos, set<vector<int>> &res) { if(pos == nums.size()) res.insert(nums); for(int i=pos;i<nums.size();++i) { if (i != pos && nums[i] == nums[pos]) continue; swap(nums[pos], nums[i]); permuteDfs(nums, pos+1, res); swap(nums[pos], nums[i]); } } };