Leetccode77:
题目描述:
给定两个整数 n
和 k
,返回范围 [1, n]
中所有可能的 k
个数的组合。
你可以按 任何顺序 返回答案。
代码及注释详解:
vector<vector<int>>ans;
vector<int>v;
vector<vector<int>> combine(int n, int k) {
dfs(0, 1, k, n);
return ans;
}
//i:已经添加了几个元素了
//m:从m开始向后选择
//k:目标枚举个数
//n:最大元素值
void dfs(int i, int m, int k, int n) {
//已经找到了一个组合
if (i == k) {
ans.push_back(v);
return;
}
//当前位置选择元素
for (int j = m; j <= n; j++) {
v.push_back(j);
//向后递归
dfs(i + 1, j + 1, k, n);
//回溯
v.pop_back();
}
}
Leetccode216:
题目描述:
找出所有相加之和为 n
的 k
个数的组合,且满足下列条件:
- 只使用数字1到9
- 每个数字 最多使用一次
返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次,组合可以以任何顺序返回。
代码及注释详解:
vector<vector<int>>ans;
vector<int>temp;
vector<vector<int>> combinationSum3(int k, int n) {
dfs(0, 1, k, n);
return ans;
}
//i:第几个元素了
//j:从哪个元素可以开始选择
void dfs(int i, int j, int k, int n) {
if (i == k) {
//是一个完整的组合,判断元素总和是否达到要求
int sum = 0;
for (int j = 0; j < k; j++) {
sum += temp[j];
}
if (sum == n)
ans.push_back(temp);
return;
}
//枚举当前层每一种情况
for (int select = j; select <= 9 && (9 - select + 1) >= (k - i); select++) {
temp.push_back(select);
//递归到下一层
dfs(i + 1, select + 1, k, n);
//回溯
temp.pop_back();
}
}
Leetccode17:
题目描述:
给定一个仅包含数字 2-9
的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
代码及注释详解:
//使用二维数组存储方便使用
char c[10][4] = {
{0},
{0},
{'a','b','c'},
{'d','e','f'},
{'g','h','i'},
{'j','k','l'},
{'m','n','o'},
{'p','q','r','s'},
{'t','u','v'},
{'w','x','y','z'}
};
vector<string>ans;
//i:到那个数字下标了
void dfs(int i, string digits, string temp) {
//此时说明到字符串末尾了,结束搜索
if (!digits[i]) {
//加入答案
ans.push_back(temp);
return;
}
//转成数字
int k = digits[i] - '0';
//循环遍历该数字对应的字母
for (int j = 0; j < 4 && c[k][j]; j++) {
//对每个字母进行递归搜索
dfs(i + 1, digits, temp + c[k][j]);
}
}
vector<string> letterCombinations(string digits) {
if (digits.empty())return ans;
int len = digits.length();
dfs(0, digits, "");
return ans;
}
Leetccode39:
题目描述:
给你一个 无重复元素 的整数数组 candidates
和一个目标整数 target
,找出 candidates
中可以使数字和为目标数 target
的 所有 不同组合 ,并以列表形式返回。你可以按 任意顺序 返回这些组合。
candidates
中的 同一个 数字可以 无限制重复被选取 。如果至少一个数字的被选数量不同,则两种组合是不同的。
对于给定的输入,保证和为 target
的不同组合数少于 150
个。
代码及注释详解:
vector<vector<int>>ans;
vector<int>temp;
//i:当前可选择的最小下标
//sum:走过的路径总和
//tar目标和
//n数组大小
//candidates数组
void dfs(int i, int sum, int tar, int n, vector<int>& candidates) {
//找到满足和为tar的路径了
if (sum == tar) {
//加入路径,结束搜索
ans.push_back(temp);
return;
}
//遍历枚举可以当前层可以走的所有路径
for (int j = i; j < n; j++) {
//如果大于目标值说明该路径不能走,直接跳过
if (sum + candidates[j] > tar)continue;
//否则可以走
temp.push_back(candidates[j]);
//往下递归
dfs(j, sum + candidates[j], tar, n, candidates);
temp.pop_back();
}
}
vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
dfs(0, 0, target, candidates.size(), candidates);
return ans;
}
Leetccode40:
题目描述:
给定一个候选人编号的集合 candidates
和一个目标数 target
,找出 candidates
中所有可以使数字和为 target
的组合。
candidates
中的每个数字在每个组合中只能使用 一次 。
注意:解集不能包含重复的组合。
代码及注释详解:
//存储答案的数组
vector<vector<int>>ans;
//存储当前路径的答案数组
vector<int>temp;
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
//排序方便剪枝优化
sort(candidates.begin(), candidates.end());
dfs(0, 0, target, candidates, candidates.size());
return ans;
}
//dfs每个参数解释
//index:代表当前可以选择元素的最小下标,刚开始可以选[0,n)
//sum:表示当前总和
//target:目标总和
//元素数组
//n:数组大小
void dfs(int index, int sum, int target, vector<int>& candidates, int n) {
//找到目标路径
if (sum == target) {
ans.push_back(temp);
return;
}
//没有找到继续往下找
for (int i = index; i < n; i++) {
//剪枝
//如果能选择的最小元素+sum都大于目标值,之后的递归都不可能满足要求
if (sum + candidates[i] > target)break;
//去除重复组合,这个for循环相当于我们在枚举子孩子,如果枚举到两个相同的子孩子则需要去重
//举个例子!!!
// [1 1 2] 找3的组合(没有重复组合 )
// 根
// 1 1(这个1就相当于重复的子孩子) 2
// 1 2 2
//2
if (i > index && candidates[i] == candidates[i - 1])continue;
//递归
temp.push_back(candidates[i]);
dfs(i + 1, sum + candidates[i], target, candidates, n);
//回溯
temp.pop_back();
}
}
Leetccode131:
题目描述:
给你一个字符串 s
,请你将 s
分割成一些子串,使每个子串都是
回文串
。返回 s
所有可能的分割方案。
代码及注释详解:
vector<vector<string>>ans;
vector<string>path;
vector<vector<string>> partition(string s) {
dfs(0, s);
return ans;
}
//left当前要分割的起始下标
void dfs(int left, string s) {
//没有需要分割的部分了,切割完成,并且每一部分都为回文子串
if (left == s.size()) {
ans.push_back(path);
return;
}
//[left,right]切割字串区间,从一个开始切割
int right = left;
//只要小于size,right就能一直向后扩展
while (right < s.size()) {
//判断当前区间是否为回文串
int i = left, j = right;
bool k = true;
while (i < j) {
if (s[i] != s[j]) {
k = false;
break;
}
i++; j--;
}
//如果是回文串
if (k) {
path.push_back(s.substr(left, right - left + 1));
//递归搜索下一个区间
dfs(right + 1, s);
path.pop_back();
}
//继续增加长度
right++;
}
}
Leetccode93:
题目描述:
有效 IP 地址 正好由四个整数(每个整数位于 0
到 255
之间组成,且不能含有前导 0
),整数之间用 '.'
分隔。
- 例如:
"0.1.2.201"
和"192.168.1.1"
是 有效 IP 地址,但是"0.011.255.245"
、"192.168.1.312"
和"192.168@1.1"
是 无效 IP 地址。
给定一个只包含数字的字符串 s
,用以表示一个 IP 地址,返回所有可能的有效 IP 地址,这些地址可以通过在 s
中插入 '.'
来形成。你 不能 重新排序或删除 s
中的任何数字。你可以按 任何 顺序返回答案。
代码及注释详解:
vector<string>ans;
vector<string> restoreIpAddresses(string s) {
dfs(0, 0, "", s);
return ans;
}
//num表示枚举到第几个整数了 num范围是{0,1,2,3}
//left代表当前截断的起点
//path保存之前枚举的结果
//s即字符串
void dfs(int num, int left, string path, string s) {
//枚举结束,得到了一个IP地址
if (num == 4) {
//如果该IP地址完整,即使用了字符串里面的全部数字
if (left == s.size()) {
//去掉最后一个'.'字符
//因为每次添加一个整数都是path+"整数."后面跟一个字符'.'
path[path.size() - 1] = '\0';
ans.push_back(path);
}
return;
}
//从当前开始依次枚举
//[left,left+0],[left,left+1],[left,left+2]这个三个子字符串作为整数
for (int i = 0; i < 3; i++) {
int right = left + i;
//剪枝,举个例子
//第一个整数只选2,"2."
//剩下三个整数无论怎么选,最大只能"2.552.551.113"也用不完所有数
//因此无法满足直接跳过该情况
if ((4 - num - 1) * 3 < s.size() - right - 1)continue;
//防止越界
if (right >= s.size())break;
//如果该整数起点为0,则只能选0
if (s[left] == '0') {
dfs(num + 1, left + 1, path + "0.", s);
//跳出循环
break;
}
//正常情况
else {
//获取所要整数
int data = stoi(s.substr(left, right - left + 1));
//判断是否合法
if (data <= 255)
dfs(num + 1, right + 1, path + s.substr(left, right - left + 1) + ".", s);
}
}
}
Leetccode78:
题目描述:
给你一个整数数组 nums
,数组中的元素 互不相同 。返回该数组所有可能的
子集
(幂集)。
解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。
代码及注释详解:
//总子集集合
vector<vector<int>>ans;
//当前子集
vector<int>path;
vector<vector<int>> subsets(vector<int>& nums) {
//首先添加空集
//空集是所有集合的子集
ans.push_back(path);
//枚举所有子集
dfs(0, nums);
return ans;
}
//left,当前层可以添加的最小下标元素
void dfs(int left, vector<int>& nums) {
if (left == nums.size()) {
return;
}
//遍历当前层可以添加的所有元素
for (int i = left; i < nums.size(); i++) {
//当前子集每添加一个元素都是一个新的子集
path.push_back(nums[i]);
ans.push_back(path);
//递归搜索下一层
dfs(i + 1, nums);
path.pop_back();
}
}
Leetccode90:
题目描述:
给你一个整数数组 nums
,其中可能包含重复元素,请你返回该数组所有可能的
子集
(幂集)。
解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。
代码及注释详解:
vector<vector<int>>ans;
vector<int>path;
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
//由于该集合可能存在重复元素,排序是为了方便去重
sort(nums.begin(), nums.end());
ans.push_back(path);
dfs(0, nums);
return ans;
}
//
void dfs(int left, vector<int>& nums) {
if (left == nums.size()) {
return;
}
for (int i = left; i < nums.size(); i++) {
//当前层已经添加过该元素了,所以应该去重
if (i > left && nums[i] == nums[i - 1])continue;
path.push_back(nums[i]);
ans.push_back(path);
dfs(i + 1, nums);
path.pop_back();
}
}
Leetccode491:
题目描述:
给你一个整数数组 nums
,找出并返回所有该数组中不同的递增子序列,递增子序列中 至少有两个元素 。你可以按 任意顺序 返回答案。
数组中可能含有重复元素,如出现两个整数相等,也可以视作递增序列的一种特殊情况。
代码及注释详解:
vector<vector<int>>ans;
vector<int>path;
vector<vector<int>> findSubsequences(vector<int>& nums) {
dfs(0, nums);
return ans;
}
void dfs(int left, vector<int>& nums) {
if (left == nums.size()) {
return;
}
//树层去重 m3之前题目都有提到的老弟
unordered_set<int>uset;
for (int i = left; i < nums.size(); i++) {
if (left > 0 && nums[i] < nums[left - 1])continue;
if (uset.find(nums[i]) != uset.end())continue;
uset.insert(nums[i]);
path.push_back(nums[i]);
if (path.size() > 1)ans.push_back(path);
dfs(i + 1, nums);
path.pop_back();
}
}
Leetccode46:
题目描述:
给定一个不含重复数字的数组 nums
,返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。
代码及注释详解:
vector<int>temp;
vector<vector<int>>ans;
//2^n到n的映射
unordered_map<int, int>umap;
vector<vector<int>> permute(vector<int>& nums) {
for (int i = 0; i <= nums.size(); i++) {
umap[1 << i] = i;
}
f(0, nums.size(), nums, (1 << (nums.size() + 1)) - 2);
return ans;
}
//i:选到哪个位置了,总共有[0,m-1]m个位置
//key是二进制状态码,状态压缩 1110代表1、2、3这个几个为都没有被使用过
void f(int i, int m, vector<int>& nums, int key) {
//选完了
if (i == m) {
ans.push_back(temp);
return;
}
//使用状态码快速找到可以使用的元素下标
for (int k = key; k; k ^= (k & -k)) {
int o = k & -k;
//2^n到n的映射
//j表示选第几个元素
int j = umap[o];
//第n个元素 下标对应n-1
temp.push_back(nums[j - 1]);
//往下搜索
f(i + 1, m, nums, key - o);
temp.pop_back();
}
}
Leetccode47:
题目描述:
给定一个可包含重复数字的序列 nums
,按任意顺序 返回所有不重复的全排列。
代码及注释详解:
vector<vector<int>>ans;
vector<int>path;
int vis[10];
vector<vector<int>> permuteUnique(vector<int>& nums) {
//排序方便去重
sort(nums.begin(), nums.end());
dfs(0, nums);
return ans;
}
void dfs(int left, vector<int>& nums) {
if (left == nums.size()) {
ans.push_back(path);
return;
}
for (int i = 0; i < nums.size(); i++) {
//该元素已经使用过了
if (vis[i] == 1)continue;
//当前层已经枚举过这个子孩子了、去重
if (i > 0 && nums[i] == nums[i - 1] && vis[i - 1] != 0)continue;
vis[i] = 1;
path.push_back(nums[i]);
dfs(left + 1, nums);
path.pop_back();
vis[i] = 0;
}
}