一,求子集【无重复】
1.选择放入,继续递归
2.选择不放入,继续递归
3.本来选择放入,再选择一次不放入的这个过程,称为回溯试探法。
4.什么时候开始回溯,回溯过程
5.如何生成子集的
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector<int> >result;//存储最终的结果,返回值
vector<int> item;//回溯时,产生各个子集的数组
result.push_back(item);//加入空集
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]是不断累积的
result.push_back(item);
generate(i+1,nums,item,result);//第一次递归调用
item.pop_back();
generate(i+1,nums,item,result);//弹出后第二次递归调用
}
};
作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/subsets/solution/qiu-zi-ji-by-xia-mu-lao-zhang-ren-rt5x/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
方法2:【位运算】
1.所有集合的表示
2.元素左移什么意思,下面有
3.&运算,找到该元素是否出现过并记录
class Solution {
public:
vector<vector<int>> subsets(vector<int>& nums) {
vector<vector <int> >result;
int all_set=1<<nums.size();//所有集合,2^n
for(int i=0;i<all_set;i++)
{
vector<int> item;
for(int j=0;j<nums.size();j++)
{
if(i&(1<<j))//j代表元素下标,1《j就是二进制,0011《《2=1100
{
item.push_back(nums[j]);
}
}
result.push_back(item);
}
return result;
}
};
二,子集II【有重复】
1.两个造成数组重复的原因
2.排序是为了什么
3.set去重
class Solution {
public:
vector<vector<int>> subsetsWithDup(vector<int>& nums) {
vector<vector<int> >result;
vector<int>item;
set<vector<int> >res_set;//去重使用的集合set
sort(nums.begin(),nums.end());
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())
{
result.push_back(item);
res_set.insert(item);//将item放入去重集合set中
}
generate(i+1,nums,item,res_set,result);
item.pop_back();
generate(i+1,nums,item,res_set,result);
}
};
作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/subsets-ii/solution/zi-ji-ii-by-xia-mu-lao-zhang-ren-bweu/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
三,组合总和II【剪枝】
LeetCode 40. Combination Sum II
1.学会优化
2.回溯-剪枝
class Solution {
public:
vector<vector<int>> combinationSum2(vector<int>& candidates, int target) {
vector<vector<int> >result;
set<vector<int> > res_set;
vector<int>item;//指的是当前组合,向后走的话还会累积
sort(candidates.begin(),candidates.end());
generate(0,item,candidates,res_set,0,result,target);
return result;
}
private:
void generate(int i,vector<int>&item,vector<int>&nums, set<vector<int> >&res_set,int sum,vector<vector<int> >&result,int target)
{
if(i>=nums.size()||sum>target)//递归结束条件||回溯条件,【减枝】
{
return;
}
sum+=nums[i];
item.push_back(nums[i]);
if(target==sum&&res_set.find(item)==res_set.end())
{
result.push_back(item);
res_set.insert(item);
}
generate(i+1,item,nums,res_set,sum,result,target);
item.pop_back();
sum-=nums[i];
generate(i+1,item,nums,res_set,sum,result,target);//【和上一个i+1不是同一个,是上一次递归的i?????】
}
};
作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/combination-sum-ii/solution/zi-ji-ii-by-xia-mu-lao-zhang-ren-h5fg/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
四,生成合法括号
LeetCode 22. Generate Parentheses
1.如何生成所有括号-递归
调用两个递归,选左或者右
2.算法思路
所有可能中,哪些是合法的
递归的限制条件
回溯条件
3.体会回溯
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);
}//left一直减少,直到left=0,之后开始回溯
if(left<right)//left从0开始往回退,只要发现left《right,就匹配
{
generate(item+")",left,right-1,result);
}
}
};
作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/generate-parentheses/solution/gua-hao-sheng-cheng-by-xia-mu-lao-zhang-owmig/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
五,N皇后
1.方向数组
2.什么时候需要回溯,回溯的条件
3.好好体会回溯的感觉,如何靠递归完成回溯’
4.注意保存镜像
5.结束条件:当递归可以完成N行的N个皇后放置,则将该结果保存并返回。
6.一层一层往下走,每层都保留着信息
class Solution {
public:
vector<vector<string>> solveNQueens(int n) {
vector< vector<string> > result;//存储最终结果的数组
vector< vector<int> > mark;//标记棋盘是否可以放置皇后的二维数组
vector<string> location;//存储某个摆放结果,当完成一次递归找到结果后,将其push到result;
for(int i=0;i<n;i++)
{
mark.push_back(vector<int>());
for(int j=0;j<n;j++)
{
mark[i].push_back(0);//mark初始化二维数组初始化
}
location.push_back("");
location[i].append(n,'.');//location初始化
}
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};
static const int dy[]={0,0,-1,1,-1,1,-1,1};//方向数组
mark[x][y]=1;//(x,y)放置皇后 进行标记
for(int i=1;i<mark.size();i++)//每个已经放置的皇后的每个方向向外延申1至N-1//i=1,从移动一个单位开始//没有n,所以用的是size()
{
for(int j=0;j<8;j++)//8个方向
{
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,//k代表完成了几个皇后的放置,正在放置第k行皇后
vector<string > &location,//
vector<vector<string> >&result,
vector<vector<int> >&mark)
{
if(k==n)//递归结束条件
{
result.push_back(location);
return;
}
for(int i=0;i<n;i++)//按顺序尝试第0至n-1列
{
if(mark[k][i]==0)//说明有位置可以放,这时皇后还没放【跳出递归了,没有地方放了】
{
vector<vector<int> >tmp_mark=mark;//记录回溯前的mark镜像
location[k][i]='Q';
put_down_the_queen(k,i,mark);//记录q放在了第k行,第i列
generate(k+1,n,location,result,mark);
//如果还能往下走,说明跳出当前递归了,即失败了,要回溯
mark=tmp_mark;
location[k][i]='.';
}
}
}
};
作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/n-queens/solution/nhuang-hou-by-xia-mu-lao-zhang-ren-n9so/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
六,逆序数【待优化】
LeetCode 315. Count of Smaller Numbers After Self
1.归并排序
#include<iostream>
#include<vector>
using namespace std;
void MergeArray(vector<int>& v,int first,int mid,int last){//有序数组的合并
vector<int> tmp(last-first+1,0);
int i = first,j = mid+1;
int k = 0;
while(i <= mid && j <= last){
if(v[i] <= v[j]) tmp[k++] = v[i++];
else tmp[k++] = v[j++];
}
while(i <= mid)//有剩余
tmp[k++] = v[i++];
while(j <= last)
tmp[k++] = v[j++];
for(int i = 0;i < k;i++) v[i+first] = tmp[i];//first是每段的开始,表示每段从头开始放入k(i+j)个
}
void MergeSort(vector<int>& v,int first,int last){//递归分半
if(first < last){
int mid = (first+last)/2;
MergeSort(v,first,mid);
MergeSort(v,mid+1,last);
MergeArray(v,first,mid,last);
}
}
int main(){
vector<int> v = {48,6,57,88,60,42,83,73,88,85};
MergeSort(v,0,v.size()-1);
for(auto&i:v) cout<<i<<" ";
}
2.一层层拆解,一层层合并
3.分治算法思想
4.count的计数,分组累加
5.count和nums下标的绑定
6.vector.clear()的作用–【重点】
看下标
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,
vector<pair<int,int> >&sub_vec2,
vector<pair<int,int> >&vec,//怎么共享的
vector<int>&count)
{
int i=0;
int j=0;
while(i<sub_vec1.size()&&j<sub_vec2.size())
{
if(sub_vec1[i].first<=sub_vec2[j].first)
{
count[sub_vec1[i].second]+=j;//下轮归并;不同组别,都是没有比较过的,累加到上次大于的数目【比较过的大于的数目】后面可能还有比它小的元素,我们将它和剩下的元素整体推入 merged 即可,让递归继续做就好【都能比得到,自己体会吧】【相较于归并,多出的统计数量的步骤】
vec.push_back(sub_vec1[i]);
i++;//没有小的了,比下一个吧
}
else
{
vec.push_back(sub_vec2[j]);
j++;
}
}
for(;i<sub_vec1.size();i++)//省略了i的参数,是之前剩下的
{
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;
vector< pair<int,int> >sub_vec2;
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();//清除了后面还能用【vector.clear()的真正作用是:把size设置成0,capacity不变】【数也不动,再添加了才动】merge_sort_two_vec(sub_vec1,sub_vec2,vec,count);
}
};
作者:xia-mu-lao-zhang-ren
链接:https://leetcode-cn.com/problems/count-of-smaller-numbers-after-self/solution/ni-xu-shu-dai-you-hua-by-xia-mu-lao-zhan-ukzb/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。