$A_n^k$与$C_n^k$(排列与组合)
题目大意:给定两个整数 n
和 k
,求$C_n^k$ 。原题链接-力扣77
1 2 3 4 5 6 7 8 9 10
| 输入:n = 4, k = 2 输出:
|
分析
用dfs,数组a[]
记录一个组合结果,v[]
记录当前数字是否访问过了。
代码
如果没有start
参数,即dfs(int n, int k, int c)
,并且for
循环中的i=start
改成i=1
,那么就是全排列,也就是$A_n^k$。
为什么呢?因为相当于在每一层中做选择的时候,可选择的数字不再局限于比前一层数字大的数字了,而是可以从所有1~n个数字中选择。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| class Solution { public: int a[25],v[25]; vector<vector<int>> res;
void dfs(int n, int k, int c, int start){ if(c==k){ vector<int> v; for(int i=0;i<k;i++) { v.push_back(a[i]); } res.push_back(v); return; } for(int i=start;i<=n;i++){ if(!v[i]){ v[i]=1; a[c]=i; dfs(n, k, c+1, i+1); v[i]=0; } }
} vector<vector<int>> combine(int n, int k) { memset(a, 0, sizeof(a)); memset(v, 0, sizeof(v)); dfs(n,k,0,1); return res; } };
|
电话号码的字母组合
原题链接-力扣17
题目大意:输入是由数字2-9
组成的字符串,返回对应的手机九键中,所有它能表示的字母组合。答案可以按 任意顺序 返回。
1 2
| 输入:digits = "23" 输出:["ad","ae","af","bd","be","bf","cd","ce","cf"]
|
分析
选择dfs的理由:
- 对每一位数字,都有几个对应的字母供它选择(横向选择)。
- 逐层递进,直到所有位的数字都选出对应的字母(纵向前进)。
这题没有v[]数字数组,因为每个数字都在不同的几个字母中挑选,不存在选到其他层的字母的问题。此外,记录路径的a[]数组也改用字符串s来记录路径。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| class Solution { public: const string letter[10] = { "", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz", }; int num[10]; int len; string s; vector<string> res; void dfs(int c){ if(c==len){ res.push_back(s); return; } for(int i=0; i<letter[num[c]].length(); i++){ s.push_back(letter[num[c]][i]); dfs(c+1); s.pop_back(); } } vector<string> letterCombinations(string digits) { if(digits.length()==0) return {}; len=digits.length(); for(int i=0; i<len; i++){ num[i] = digits[i]-'0'; } dfs(0); return res; } };
|
ps:string
也可以像vector
一样用push_back(x)
和pop_back()
。
组合总和(候选无重复,选取可重复)
原题链接-力扣17
题目大意:给你一个无重复元素的数组 candidates
,找出 candidates
中可以使数字和为 target 的所有不同组合。可以按任意顺序返回这些组合。candidates
中的数字可以被重复选取 。
1 2 3 4 5 6
| 输入:candidates = [2,3,6,7], target = 7 输出:[[2,2,3], [7]] 解释: 2 和 3 可以形成一组候选,2 + 2 + 3 = 7 。注意 2 可以使用多次。 7 也是一个候选, 7 = 7 。 仅有这两种组合。
|
分析
每个数字可以被重复选取,也就不用v[]
数组了;对结果的个数没有要求,只要能凑出target
都行,所以dfs
的参数中也不需要记录当前的层数c了,另外需要添加start参数保证结果是“组合”而非“排列”。这题其实条件很松弛,写起来更简单。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| class Solution { public: vector<vector<int>> res; vector<int> v; void dfs(vector<int>& candidates, int rest, int start){ if(rest<0) return; if(rest==0){ res.push_back(v); return; } for(int i=start;i<candidates.size();i++){ v.push_back(candidates[i]); dfs(candidates, rest-candidates[i], i); v.pop_back(); }
} vector<vector<int>> combinationSum(vector<int>& candidates, int target) { dfs(candidates, target, 0); return res; } };
|
组合总和II(候选有重复,选取无重复)
原题链接-力扣40
题目大意:给你一个有重复元素的数组 candidates
,找出 candidates
中可以使数字和为 target 的所有不同组合。可以按任意顺序返回这些组合。candidates
中的数字不可以重复选取 。
1 2 3
| 输入: candidates = , target = 8, 输出: 解释: 虽然candidates数组中有两个1,但是不能输出两次。
|
分析
先对candidates排序,使得相同的元素挨在一起,这样做的目的是为了更容易找到相同的元素,以便于将重复的情况剔除。
与上一题相比,增加了两处关键代码:
- 开始
dfs
之前,先sort()
- 横向选择时,增加if条件判断:当
i>start
且candidates[i] == candidates[i-1]
时,直接continue
,剪掉整个枝
看代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
| class Solution { public: vector<vector<int>> res; vector<int> vec; void dfs(vector<int>& candidates, int rest, int start){ if(rest<0) return; if(rest==0){ res.push_back(vec); return; } for(int i=start; i<candidates.size(); i++){ if(i>start && candidates[i] == candidates[i-1]) continue; vec.push_back(candidates[i]); dfs(candidates, rest-candidates[i], i+1); vec.pop_back(); } } vector<vector<int>> combinationSum2(vector<int>& candidates, int target) { sort(candidates.begin(), candidates.end()); dfs(candidates, target, 0); return res; } };
|