基本概念
leetcode题目
78. Subsets
题意:
给出一个数列,求出这个数列所有子集
解题思路:
用递归解决,设计思路:
数据结构:
一个vector<int>
item存储集合
一个vector<vector<int>>
result 存储结果
递归程序:
generate(i,nums,item,result)
递归程序含义:nums[i]前所有元素+nums[i]后所有元素组成的所有子集放入result中
结束条件:i=nums.size()
递归过程:
item.push_back(nums[i]);
generate(i+1,nums,item,result);
item.pop(nums[i]);
generate(i+1,nums,item,result);
代码:
class Solution {
public:
std::vector<std::vector<int> > subsets(std::vector<int>& nums) {
std::vector<std::vector<int> > result;
std::vector<int> item;
result.push_back(item);//先将空集压入
generate(0, nums, item, result);
return result;
}
private:
void generate(int i, std::vector<int>& nums,
std::vector<int> &item,
std::vector<std::vector<int> > &result){
if (i >= nums.size()){
return;
}
item.push_back(nums[i]);
result.push_back(item);
generate(i + 1, nums, item, result);
item.pop_back();
generate(i + 1, nums, item, result);
}
};
思路2:位运算法(循环解法)
位运算基本操作:
解题思路:
如果集合内有n个数,那么就有2^n个子集
从0到2^(n-1)循环,用二进制表示,每一个数表示一种集合。
每一个数根据每个位,遍历n个数是否在所表示的集合内。
代码:
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int>> result;
int n=nums.size();
int all_set=1<<n;
for(int i=0;i<all_set;i++){
vector<int> item;
for(int j=0;j<n;j++){
if(i&(1<<j)) item.push_back(nums[j]);
}
result.push_back(item);
}
return result;
}
};
90. Subsets II
题意:
找子集,要求子集没有重复。
解题思路:
有两种重复的子集,一种是元素和顺序都相同,一种只有元素相同
1:[1,2,2]和[1,2,2]
2:[1,2,2]和[2,1,2]
对于第一种重复,可以用set去除
对于第二种重复,可以先对元素进行排序,使它只剩下第一种重复
代码:
class Solution {
public:
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
sort(nums.begin(),nums.end());
vector<vector<int>> result;
set<vector<int>> res_set;
vector<int> item;
result.push_back(item);
generate(0,nums,item,res_set,result);
return result;
}
private:
void generate(int i,vector<int>& nums,vector<int> &item,set<vector<int>> &res_set,vector<vector<int>> &result){
if(i==nums.size()) return;
item.push_back(nums[i]);
if(res_set.find(item)==res_set.end()){
res_set.insert(item);
result.push_back(item);
}
generate(i+1,nums,item,res_set,result);
item.pop_back();
generate(i+1,nums,item,res_set,result);
}
};
40. Combination Sum II
题意:
找到相加之为和为T的子集。
解题思路
运用减枝的思想,缩减递归运算的时间复杂度,就是已经不满足的条件的分枝不再进行递归。
代码:
class Solution {
public:
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
vector<int> item;
vector<vector<int>> result;
set<vector<int>> res_set;
sort(candidates.begin(),candidates.end());
generate(0,candidates,item,result,res_set,0,target);
return result;
}
private:
void generate(int i,vector<int> &candidates, vector<int> &item,vector<vector<int>> &result,set<vector<int>> &res_set,int sum,int target){
if(i>=candidates.size()||sum>target) return;
item.push_back(candidates[i]);
sum+=candidates[i];
if(res_set.find(item)==res_set.end()&&sum==target){
res_set.insert(item);
result.push_back(item);
}
generate(i+1,candidates,item,result,res_set,sum,target);
item.pop_back();
sum-=candidates[i];
generate(i+1,candidates,item,result,res_set,sum,target);
}
};
22. Generate Parentheses
题意:
输入括号数n,输出所有合法的括号组合的集合。
解题思路:
用递归的方法解:
每次加一个 ‘(’ 或者‘ ) ’,加‘)’的条件是‘(’的数目比较多。
递归结束条件:当字符串长度达到2n
用left记录左扩号剩余数目
用right记录右括号剩余数目
每次递归left-1或者right-1,用条件过滤掉不满足条件的项。
代码:
class Solution {
public:
vector<string> generateParenthesis(int n) {
vector<string> result;
generate("",n,n,result);
return result;
}
private:
void generate(string item,int left,int right,vector<string> &result){
if(left==0&&right==0) {
result.push_back(item);
return;
}
if(left>0){
generate(item+'(',left-1,right,result);
}
if(left<right){
generate(item+')',left,right-1,result);
}
}
};
51. N-Queens
题意:
在国际象棋n*n的棋盘上放置n个皇后,使它们互相不在攻击范围之内。
解题基础:
方向数组:
每放下一个皇后,它的上下左右和两个对角线,一共8个方向都不能放置,用一个二维数组记录能放置的位置为0,不能的为1。如果仅仅用循环,代码比较复杂,应该用方向数组:
static const int dx[] = {-1, 1, 0, 0, -1, -1, 1, 1};
static const int dy[] = {0, 0, -1, 1, -1, 1, -1, 1};
dx[i],dy[i]表示一个方向,用两个循环,一个循环遍历所有的方向,一个循环遍历方向上所有的数。
static的意思是静态分配内存,就是对于经常使用的相同的常量,只分配一次空间,不用每次调用都分配一次内存。
void put_down_the_queen(int x, int y,std::vector<std::vector<int> > &mark){
static const int dx[] = {-1, 1, 0, 0, -1, -1, 1, 1};
static const int dy[] = {0, 0, -1, 1, -1, 1, -1, 1};
mark[x][y] = 1;
for (int i = 1; i < mark.size(); i++){
for (int j = 0; j < 8; j++){
int new_x = x + i * dx[j];
int new_y = y + i * dy[j];
if (new_x >= 0 && new_x < mark.size() &&
new_y >= 0 && new_y < mark.size()){
mark[new_x][new_y] = 1;
}
}
}
}
解题思路:
数据结构:
location储存result的图
mark 存储可以放的位置
步骤:
1 先初始化各个数据结构。
2 写好放皇后的方法,放入皇后同时改变location和mark数组
3 每次在第k行找一个位置放皇后,遍历第k行的所有列,递归调用地找第k+1行中的皇后。
递归结束条件:k==n;
递归过程:
generate(k, location, mark,result)
用一个循环遍历k行中所有列,若有为0的列,递归调用generate
递归前需要设置mark数组,所以要先用临时mark储存mark,递归调用后还原mark,使每次循环的mark不变。
代码:
class Solution {
public:
std::vector<std::vector<std::string> > solveNQueens(int n) {
std::vector<std::vector<std::string> > result;
std::vector<std::vector<int> > mark;
std::vector<std::string> location;
for (int i = 0; i < n; i++){
mark.push_back((std::vector<int>()));
for (int j = 0; j < n; j++){
mark[i].push_back(0);
}
location.push_back("");
location[i].append(n, '.');
}
generate(0, n, location, result, mark);
return result;
}
private:
void put_down_the_queen(int x, int y,
std::vector<std::vector<int> > &mark){
static const int dx[] = {-1, 1, 0, 0, -1, -1, 1, 1};
static const int dy[] = {0, 0, -1, 1, -1, 1, -1, 1};
mark[x][y] = 1;
for (int i = 1; i < mark.size(); i++){
for (int j = 0; j < 8; j++){
int new_x = x + i * dx[j];
int new_y = y + i * dy[j];
if (new_x >= 0 && new_x < mark.size() &&
new_y >= 0 && new_y < mark.size()){
mark[new_x][new_y] = 1;
}
}
}
}
void generate(int k, int n,
std::vector<std::string> &location,
std::vector<std::vector<std::string> > &result,
std::vector<std::vector<int> > &mark){
if (k == n){
result.push_back(location);
return;
}
for (int i = 0; i < n; i++){
if (mark[k][i] == 0){
std::vector<std::vector<int> > tmp_mark = mark;
location[k][i] = 'Q';
put_down_the_queen(k, i, mark);
generate(k + 1, n, location, result, mark);
mark = tmp_mark;
location[k][i] = '.';
}
}
}
};