递归-回溯-分治

【扩展】

一,求子集【无重复】

LeetCode 78. Subsets

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【有重复】

LeetCode 90. Subsets 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皇后

LeetCode 51. N-Queens

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)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值