N选K组合(无重复版) 给定两个整数 n 和 k,返回 1 … n 中所有可能的 k 个数的组合。
N选K组合解题思路 状态:第i个
选择: n-i个选择
路径:path选择
结果集:列表
结束条件: 找到所有的组合; path路径到了k
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 vector <int > path;void dfs (int u, int start_idx) { if (u == k) { return ; } for (int i = start_idx; i <= n; ++i) { path.push_back(i); dfs(u + 1 , i + 1 ) ; path.pop_back(); } for (int i = start_idx; i <= n - (k - path.size()); ++i) { path.push_back(i); dfs(u + 1 , i + 1 ); path.pop_back(); } } dfs(0 , 0 );
和为n的K个数的组合 找出所有相加之和为 n 的 k 个数的组合。组合中只允许含有 1 - 9 的正整数,并且每种组合中不存在重复的数字。
N选K组合的和解题思路 状态:第i个
选择: 9-i个选择
路径:path选择
结果集:列表
结束条件: 找到所有的组合; path路径到了k,sum ok
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 vector <int > path;void dfs (int u, int sum, int start) { if (sum > n) {return ;} if (u == k) { if (sum == 0 ) { res.push_back(path); } return ; } for (int i = start; i <= 9 - (k - path.size()) + 1 ; i++) { path.push_back(i); dfs(u + 1 , sum - i, i + 1 ); path.pop_back(); } } dfs(0 , n, 0 );
和为N的组合(无限选取版) 给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。candidates 中的数字可以无限制重复被选取。
说明:
所有数字(包括 target)都是正整数。
解集不能包含重复的组合。
candidates找和为target解题思路 状态:第i个
选择: 每次都有N个选择
路径:path选择
结果集:列表
结束条件: 找到所有的组合; path路径到了k,sum ok
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 void dfs (vector <int >& d, int u, int sum, int start) { if (sum > target) {return ;} if (sum == target) { res.push_back(path); return ; } for (int i = start; i < n; ++i) { path.push_back(d[i]); dfs(d, u + 1 , sum + d[i], i); path.pop_back(); } } void dfs (vector <int >& d, int u, int sum, int start) { if (sum > target) {return ;} if (sum == target) { res.push_back(path); return ; } for (int i = start; i < n && sum+d[i] <= target; ++i) { path.push_back(d[i]); dfs(d, u + 1 , sum + d[i], i); path.pop_back(); } }
和为N的组合(有重复,仅用一次) 给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的每个数字在每个组合中只能使用一次。
说明: 所有数字(包括目标数)都是正整数。解集不能包含重复的组合。
//需要进行排序预处理,从大到小
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void dfs (vector <int >& d, int u, int sum, int start_index) { if (sum > target) {return ;} if (sum == target) { res.push_back(path); return ; } for (int i = start_index; i < n && sum + d[i] <= target; ++i) { if (i >= 1 && d[i] == d[i - 1 ] && used[i - 1 ] == false ) {continue ;} path.push_back(d[i]); used[i] = true ; dfs(d, u + 1 , sum + d[i], i + 1 ); used[i] = false ; path.pop_back(); } }
所有数字的排列(无重复版) 给定一个 没有重复 数字的序列,返回其所有可能的全排列。
解法1 给坑u找元素i 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 void dfs (vector <int >& d, int u) { if (u == n) { res.push_back(path); return ; } for (int i = 0 ; i < n; ++i ) { if (used[i]) continue ; path.push_back(d[i]); used[i] = true ; dfs(d, u + 1 ); used[i] =false ; path.pop_back(); } }
解法2: 特定元素(u)选择坑位(i) //后面可以选择的坑原来越少了; u是第一个元素;
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 46 class Solution {public : vector <vector <int >> res; vector <int > path; void dfs (vector <int >& nums, int u, int state) { if (u == int (nums.size())) { res.push_back(path); return ; } for (int i = 0 ; i < nums.size(); ++i) { if (!(state >> i & 1 )) { path[i]= nums[u]; dfs(nums, u + 1 , state + (1 << i)); } } } vector <vector <int >> permutation (vector <int >& nums) { path.resize(nums.size()); sort(nums.begin(), nums.end()); dfs(nums, 0 , 0 , 0 ); return res; } }; 注意到前面的state与path的配合使用,有两个目的:保存当前坑位存了哪些元素了,保存坑位的使用情况。 有没有什么手段呢? path可以d来替代;d的前半部分作为坑使用,后半部分保持原功能; 但是坑占用之前,需要保存其原值,一举两得。所以就有了下面这种解法。 void dfs (vector <int >& d, int u) { if (u == n) { res.push_back(d); return ; } for (int i = u; i < n; ++i ) { swap(d[u], d[i]); dfs(d, u + 1 ); swap(d[u], d[i]); } }
所有数字的排列(数字重复) 输入一组数字(可能包含重复数字),输出其所有的排列方式。
样例 输入:[1,2,3]
输出: [ [1,2,3], [1,3,2], [2,1,3], [2,3,1], [3,1,2], [3,2,1] ]
为坑(u)选择合适的元素(i) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void dfs (int u) { if (u == nums.size()) { res.push_back(path); return ; } for (int i = 0 ; i < nums.size(); i++) { if (i > 0 && nums[i] == nums[i - 1 ] && !used[i - 1 ]) continue ; if (used[i]) continue ; used[i] = true ; path.push_back(nums[i]); dfs(u + 1 ); path.pop_back(); used[i] = false ; } }
特定元素(u)选择坑位(i) 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 class Solution {public : vector <vector <int >> res; vector <int > path; void dfs (vector <int >& nums, int u, int start, int state) { if (u == int (nums.size())) { res.push_back(path); return ; } if (!u || nums[u] != nums[u - 1 ]) start = 0 ; for (int i = start; i < nums.size(); ++i) { if (!(state >> i & 1 )) { path[i]= nums[u]; dfs(nums, u + 1 , i + 1 , state + (1 << i)); } } } vector <vector <int >> permutation (vector <int >& nums) { path.resize(nums.size()); sort(nums.begin(), nums.end()); dfs(nums, 0 , 0 , 0 ); return res; } };
所有递增子序列 给定一个整型数组, 你的任务是找到所有该数组的递增子序列,递增子序列的长度至少是2。
示例:
输入: [4, 6, 7, 7] 输出: [[4, 6], [4, 7], [4, 6, 7], [4, 6, 7, 7], [6, 7], [6, 7, 7], [7,7], [4,7,7]]
说明:
给定数组的长度不会超过15。 数组中的整数范围是 [-100,100]。 给定数组中可能包含重复数字,相等的数字应该被视为递增的一种情况。
解题思路
因为找到是递增子序列,所以不能排序;也就不能通过排序来去重了
可以考虑用hash_map来做去重。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void dfs (int u, int start) { if (u >= 2 ) { res.push_back(path); } unordered_set <int > iset; for (int i = start; i < d.size(); i++) { if (iset.count(d[i])) continue ; if (path.size() && path.back() > d[i]) continue ; iset.insert(d[i]); path.push_back(d[i]); dfs(u + 1 , i + 1 ); path.pop_back(); } }
https://zhuanlan.zhihu.com/p/339849416