1-a 求子集1 LeetCode 78
题目:
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
方法1:回溯法(O(2^n))
class Solution{
public:
vector<vector<int>> subsets1(vector<int>& nums){
vector<vector<int>> result;//存储最终结果
vector<int> item;//回溯时产生的临时数组
result.push_back(item);//将空集push进result数组
generate(0, nums, item, result);//计算各个子集
return result;
}
private:
void generate(int i,vector<int>& nums,vector<int> &item,vector<vector<int>> &result){
if(i>=nums.size()){//跳出递归条件
return;
}
item.push_back(nums[i]);//将nums中第i个数字push进item中
result.push_back(item);//将item中的内容push进result中
generate(i+1, nums, item,result);//递归调用寻找子集的函数
item.pop_back();//将前个数字pop出,再调用递归函数,这样对于nums中的数字而言每个数字都进行过选择或者不选择的组合
generate(i+1, nums, item,result);
}
};
方法2:位运算法
简单的说就是将要判断的整数数组看成一串可以为(0或者1)的二进制数,计算不同位置为0为1的组合则是该位置数字出现或者不出现的组合
vector<vector<int>> subsets2(vector<int>& nums){
vector<vector<int>> res;//存储最终结果
int all_set = 1<<nums.size();//all_set设置为2^n n为nums的大小代表所有可能出现的情况
for(int i=0;i<all_set;i++){//遍历所有集合
vector<int> item;//子集
for(int j=0;j<nums.size();j++){
if(i&(1<<j)){//数字i代表的集合,将各个元素存进item中
//1<<j即为nums数组的第i个元素
item.push_back(nums[j]);
}
}
res.push_back(item);
}
return res;
}
1-b 求子集2 LeetCode 90
题目:
给定一个可能包含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: [1,2,2]
输出:
[
[2],
[1],
[1,2,2],
[2,2],
[1,2],
[]
]
思路:
class Solution {
public:
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
vector<vector<int>> res;//存储结果
vector<int> item;//存储子集
set<vector<int>> re_res;//临时结果
sort(nums.begin(),nums.end());//将nums从小到大排序
res.push_back(item);//将空集push进res
generate1(0,nums,res,item,re_res);//寻找子集
return res;
}
void generate1(int i, vector<int>& nums, vector<vector<int>> &res, vector<int> &item, set<vector<int>> &re_res){
if(i>=nums.size()){//跳出条件
return;
}
item.push_back(nums[i]);//将第i个数字push进item中
if(re_res.find(item)==re_res.end()){
//判断临时结果中有无item现在的子集,如果有则重复不再存进最终结果中,否则则将item存进res中,并将其插入临时结果记录
res.push_back(item);
re_res.insert(item);
}
generate1(i+1,nums,res,item,re_res);//调用递归函数
item.pop_back();//将当前数字pop再递归,这样对于nums中的数字而言每个数字都进行过选择或者不选择的组合
generate1(i+1,nums,res,item,re_res);
}
};
1-c 组合总和 LeetCode
题目:
给定一个数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。
candidates 中的每个数字在每个组合中只能使用一次。
说明:
所有数字(包括目标数)都是正整数。
解集不能包含重复的组合。
思路:
class Solution {
public:
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
vector<vector<int>> res;//存储结果
vector<int> item;//存储子集
set<vector<int>> re_res;//临时结果
sort(candidates.begin(), candidates.end());//将candidates排序
//res.push_back(item) 不用再push空集了!!
generate2(0,candidates,res,item,re_res,0,target);//寻找子集
return res;//返回结果
}
void generate2(int i, vector<int>& candidates, vector<vector<int>> &res,
vector<int> &item,set<vector<int>> &re_res,int sum, int target){
if(i >= candidates.size()|| sum > target){//跳出递归条件
return;
}
item.push_back(candidates[i]);//将candidates中的第i个数存进item
sum+=candidates[i];//sum加上item中存储的数
if(sum == target && re_res.find(item)==re_res.end()){
//当item中的数之和小于并且没有重复时,才能将item存进res中,并将item中的数放进re_res中记录
res.push_back(item);
re_res.insert(item);
}
generate2(i+1,candidates,res,item,re_res,sum,target);//调用递归
item.pop_back();//item中最后一个数字pop后再递归
sum-=candidates[i];//pop后不要忘了sum也要减掉这个数
generate2(i+1,candidates,res,item,re_res,sum,target);
}
};
2. 括号生成
题目:
给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。
例如,给出 n = 3,生成结果为:
[
“((()))”,
“(()())”,
“(())()”,
“()(())”,
“()()()”
]
思路:
#include <iostream>
#include <vector>
#include <string>
#include <stdlib.h>
using namespace std;
class Solution{
public:
vector<string> generateParenthesis(int n){
vector<string> res;//存储结果
generate("",n,n,res);//生成括号组合
return res;
}
private:
void generate(string item, int left, int right,vector<string> &res){//如果定义成了普通变量,res的值会随着此函数执行结束而被清除
if(left == 0 && right == 0){//当生成完n对括号递归结束
res.push_back(item);//将item中存储的括号对push进res
return;
}
if(left>0){//先生成左括号并且左括号最多为n个
generate(item+'(',left-1,right,res);
}
if(left < right){//后生成右括号并且生成的右括号的个数不能大于左括号的个数
generate(item+')',left,right-1,res);
}
}
};
void main()
{
Solution solve;
vector<string> result;
result = solve.generateParenthesis(3);
for(int i=0;i<result.size();i++){
cout<<result[i]<<'\n';
}
system("pause");
}
3. N皇后 LeetCode 51
题目:
n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。
上图为 8 皇后问题的一种解法。
给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。
每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
示例:
输入: 4
输出: [
[".Q…", // 解法 1
“…Q”,
“Q…”,
“…Q.”],
["…Q.", // 解法 2
“Q…”,
“…Q”,
“.Q…”]
]
解释: 4 皇后问题存在两个不同的解法。
解析:
皇后的攻击范围
回溯法
#include <iostream>
#include <vector>
#include <string>
#include <stdlib.h>
using namespace std;
class Solution{
public:
vector<vector<string>> solveNQueens(int n){
vector<vector<string>> result;//用来存储结果
vector<vector<int>> mark;//mark是用来存储是否可以摆放皇后的二维数组
vector<string> location;//location用来存储递归得到的皇后的摆放位置
for(int i=0; i < n; i++){//初始化mark和location
mark.push_back(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, vector<vector<int>> &mark){
static const int dx[] ={-1,1,0,0,-1,-1,1,1};//设置方向数组,代表8个方向
static const int dy[] ={0,0,-1,1,1,-1,1,-1};
mark[x][y]=1;
for(int i=0; i < mark.size();i++){
//扫描mark的八个方向并将其置为1,这些都是皇后可以攻击的范围,故不能再放置其他皇后
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, vector<string> &location, vector<vector<string>> &result, vector<vector<int>> &mark){
//k代表第几行
if(k==n){//如果k等于n则说明每行的皇后放置完成,将放置结果push进result返回
result.push_back(location);
return;
}
for(int i=0; i<n;i++){//依次寻找可以放皇后的位置
if(mark[k][i] == 0){//如果mark[k][i]为0则该位置可以放置
vector<vector<int>> temp_mark = mark;//申请临时数组存放当前mark
put_down_the_queen(k,i,mark);//在[k][i]处放置皇后
location[k][i] = 'Q'; //在location相应位置设为Q
generate(k+1, n, location, result, mark);//递归寻找下一行可以放置皇后的位置
mark = temp_mark;//让mark重新回到回溯前的状态
location[k][i] = '.';//location同理
}
}
}
};
void main(){
vector<vector<string>> result;
Solution solve;
result = solve.solveNQueens(4);
for(int i=0; i<result.size();i++){
cout<<"i="<<i<<'\n';
for(int j=0;j<result[i].size();j++){
cout<<result[i][j]<<'\n';
}
cout<<'\n';
}
system("pause");
}
4. 计算右侧小于当前元素的个数 LeetCode315
题目:
给定一个整数数组 nums,按要求返回一个新数组 counts。数组 counts 有该性质: counts[i] 的值是 nums[i] 右侧小于 nums[i] 的元素的数量。
示例:
输入: [5,2,6,1]
输出: [2,1,1,0]
解释:
5 的右侧有 2 个更小的元素 (2 和 1).
2 的右侧仅有 1 个更小的元素 (1).
6 的右侧有 1 个更小的元素 (1).
1 的右侧有 0 个更小的元素.
思路:
#include <iostream>
#include <vector>
#include <stdlib.h>
using namespace std;
class Solution{
public:
vector<int> countSmaller(vector<int>& nums){
vector<pair<int, int>> vec;
vector<int> count;
for(int i=0;i < nums.size();i++){
vec.push_back(make_pair(nums[i],i));
count.push_back(0);
}
merge_sort(vec, count);
return count;
}
private:
void merge_sort_two_vec(vector<pair<int, int>> &sub_vec1, //数组1 将数组1,2 排序合并
vector<pair<int, int>> &sub_vec2, //数组2
vector<pair<int, int>> &vec,
vector<int> &count){ //合并后的数组
int i=0;
int j=0;
while(i < sub_vec1.size() && j < sub_vec2.size()){//比较两个数组的大小,较小的先push进vec
if(sub_vec1[i].first <= sub_vec2[j].first){
count[sub_vec1[i].second] +=j; //在i位置后面push了多少数进来,就说明有多少数小于i位置的数字
vec.push_back(sub_vec1[i]);
i++;
}
else{
vec.push_back(sub_vec2[j]);
j++;
}
}
for(;i < sub_vec1.size();i++){//将数组1或数组2中剩余元素push进vec中
count[sub_vec1[i].second]+=j;
vec.push_back(sub_vec1[i]);
}
for(;j < sub_vec2.size(); j++){
vec.push_back(sub_vec2[j]);
}
}
void merge_sort(vector<pair<int, int>> &vec, vector<int> &count){//利用分治法对一个数组进行排序
if(vec.size() < 2){
return;
}
int mid = vec.size()/2; //拆分时,从数中间进行拆分
vector<pair<int, int>> sub_vec1;//拆分的数组1
vector<pair<int, int>> sub_vec2;//拆分的数组2
//进行拆分
for(int i=0;i<mid;i++){
sub_vec1.push_back(vec[i]);
}
for(int i=mid; i<vec.size();i++){
sub_vec2.push_back(vec[i]);
}
merge_sort(sub_vec1,count);//递归拆分
merge_sort(sub_vec2,count);//
vec.clear();//将vec现有内容清空
merge_sort_two_vec(sub_vec1, sub_vec2, vec, count);//对子数组排序
}
};
void main(){
Solution solve;
vector<int> nums;
nums.push_back(5);
nums.push_back(2);
nums.push_back(1);
nums.push_back(1);
vector<int> result = solve.countSmaller(nums);
cout<<"[";
for(int i=0;i < nums.size();i++){
cout<<result[i]<<',';
}
cout<<"]"<<endl;
system("pause");
}