一:找出所有子集的异或总和再求和
题目:给出一个整数数组,然后计算它的所有子集的异或和,最后返回其所有的异或总和
示例:
给一个整数数组nums[1,2,3],子集为[1],[2],[3],[1 2],[1 3],[1 2 3],[]。它们的子集异或后的值为:1,2,3,3,2,0,0,故而计算总和为11,进而最后结果为11
方法:
1:画出决策树(与之前求所有子集的决策树一致,不在这里展开,详情请看算法3)
2:算法原理
大致思路与求所有子集一致,全局变量不同变为:
int sum;(存放最后结果的总和)
int path;(存放每一条路径的异或和)
dfs函数设计与求子集的设计一致,无需设计递归出口
class Solution {
int sum;//最后要求的总和
int path;//记录每一个节点的异或
public:
int subsetXORSum(vector<int>& nums) {
dfs(nums, 0);
return sum;
}
void dfs(vector<int>& nums, int pos)
{
sum += path;//将每个叶子的异或值加入到sum中
//进行枚举,求子集,并且对其进行异或
for (int i = pos; i < nums.size(); i++)
{
path ^= nums[i];//得到异或的值,符号不要弄错谢谢
dfs(nums, i + 1);
//回溯,再将path进行异或
path ^= nums[i];
}
}
};
二:全排列2
题目:给出一个含有可重复数字的整数数组,返回其所有的排列
示例:
给出一个整数数组nums[1,1,2]。它所有的全排列为:[1,1,2],[1,2,1],[2,1,1]
方法:
1:画出决策树
(与之前无重复数的思路一致,不过重点在于剪枝)
2:算法原理
首先,由于存在重复数字的情况,将数组重新排列后再进行分析,可大大简化我们的思路
按照之前不含有重复数字的方法,我们可以得到所有的全排列,但是会存在重复,可以利用剪枝,减去多余的情况!
在进行思考时,要得到所有不重复的全排列,要注意维持两个情况:
(1)同一个数字不可以被重复使用
(2)在同一层,不可以有相同的数字同时被选择(最开始进行排序,也是为了更好地达到这一目的)
全局变量:
int[][] ret;(存放所有的路径)
int[] path;(记录每一条的路径)
bool check[];(记录当前是否为被使用过的路径)
剪枝:
不合法路径:当前位置已被使用,或者当前数与前一个数相同并且前一个数未被使用并且i!=0
check[i] ==true||(i!=0&&nums[i] ==nums[i-1]&&check[i-1]==false)
合法路径:当前位置未被使用并且满足i==0,或者当前数不等于前一个数或者前一个数已被使用
check[i]==false&&(i==0||nums[i]!=nums[i-1]||check[i-1]==true
递归出口
位置到了整数数组的长度
class Solution {
vector<vector<int>> ret;
vector<int> path;
bool check[8];//这个数组要给出大小
public:
vector<vector<int>> permuteUnique(vector<int>& nums) {
sort(nums.begin(),nums.end());
dfs(nums,0);
return ret;
}
void dfs(vector<int>&nums,int pos)
{
if(pos==nums.size())
{
ret.push_back(path);
return;
}
//进行枚举
for(int i = 0;i<nums.size();i++)
{
if(check[i]==false&&(i==0||nums[i]!=nums[i-1]||check[i-1]==true))
{
path.push_back(nums[i]);
check[i] = true;
dfs(nums,pos+1);
path.pop_back();
check[i] = false;
}
}
}
};
三:电话号码的字母组合
题目:给出一个只包含2~9的字符串,返回所有它能表示的字母组合,每一个字符有着各自的映射
2~“abc”,3~“def”,4~“ghi”,5~“jkl”,6~“mno”,7~“pqrs”,8~“tuv”,9~“wxyz”
示例:
给出字符串digits[2,3],表示出来的所有字符串为:“ad”,“ae”,“af”,"bd",“be”,“bf”,“cd”,“ce”,“cf”
方法:
1:画出决策树
2:算法原理
首先,要将字符串数组与要进行排序的字符串进行映射,得到它们之间的关系,可以用一个全局变量数组搞定
全局变量:
string harsh[10] = {“”,“”,“abc”,“def”,“ghi”,“jkl”,“mno”,“pqrs”,“tuv”,“wxyz”]
vector<string> ret;(最终存放的字符串数组)
string path;(记录的字符串)
dfs函数的设计:
将每个数字字符对应的字符串进行遍历,然后将其加到path的后面,进行递归即可
(本次由于是字符串,故而使用范围for进行遍历更加简便)
递归出口:
当位置等于digits的长度时,可以将path加到ret的后面,然后返回
class Solution {
string harsh[10] = {"","","abc","def","ghi","jkl","mno","pqrs","tuv","wxyz"};
vector<string> ret;
string path;
public:
vector<string> letterCombinations(string digits) {
if(digits.size()==0) return ret;
dfs(digits,0);
return ret;
}
void dfs(const string& digits,int pos)
{
if(pos==digits.size())
{
ret.push_back(path);
return;
}
for(auto ch:harsh[digits[pos]-'0'])
{
path.push_back(ch);
dfs(digits,pos+1);
path.pop_back();
}
}
};
四:括号生成
题目:数字n代表括号的对数,设计一个函数,使其可以返回所有可能且有效的括号组合
示例:
n = 3;可有{((()))},{()(())},{(())()},{(()())},{()()()}
方法:
有效的括号组合符合下列条件:
(1)左括号数量等于右括号数量
(2)从头开始的任意一个子串,左括号数量>右括号数量
1:画出决策树
2:算法原理
全局变量:
int left;(左括号的数量)
int right;(右括号的数量)
vector<string> ret;
string path;
dfs函数:
当左括号的数量大于n,不可以再添加左括号;
当右括号的数量大于左括号的数量,不可以再添加右括号
递归出口:
右括号的数量等于n
class Solution {
int left,right,_n;
vector<string> ret;
string path;
public:
vector<string> generateParenthesis(int n) {
_n = n;
dfs();
return ret;
}
void dfs()
{
if(right==_n)
{
ret.push_back(path);
return;
}
if(left<_n)
{
path.push_back('(');
left++;
dfs();
path.pop_back();
left--;
}
if(right<left)
{
path.push_back(')');
right++;
dfs();
path.pop_back();
right--;
}
}
};