回溯算法

1. 算法思想

回溯是一个带有系统性又带有跳跃性的搜索法,其基本思想是,按照深度优先策略从根节点出发,搜索解空间树,当搜索到任意节点时,先判断结点是否包含问题的解,如果不包含,则跳过以该节点为根的子树的搜索,逐层向根节点回溯,并搜索玩根节点的所有子树才结束,而如果只求问题的一个解,则只要搜索到问题的一个解就结束。

2. 应用

2.1 全排列

https://leetcode-cn.com/problems/permutations/
在这里插入图片描述
思路分析:从每个数开始一次深度优先遍历,同时创建两个容器和一个变量,两个容器记录路径和已经访问过的数据,变量记录递归的深度,当递归到底(变量等于数组大小)则开始回溯。

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        vector<vector<int>> res;
        vector<int> path;
        bool* v=new bool[nums.size()]{};
        dfs(nums,res,path,v,0);
        return res;
    }
    void dfs(vector<int>& nums,vector<vector<int>>& res,vector<int> path,bool v[],int depth)
    {
        if(depth==nums.size()){
            res.push_back(path);
            return;
        }
        for(int i=0;i<nums.size();i++)
        {
            if(!v[i])
                {
                    v[i]=true;
                    path.push_back(nums[i]);		
                    dfs(nums,res,path,v,depth+1);
                    path.pop_back();		//状态重置,即回溯到之前的状态
                    v[i]=false;
                }
        }
    }
};

2.2 字符串排列

https://leetcode-cn.com/problems/zi-fu-chuan-de-pai-lie-lcof/
在这里插入图片描述
思路分析:这题和全排列相比,只需要去重就可以了

class Solution {
public:
    vector<string> permutation(string s) {
        vector<string> res;
        if(s.empty())
            return res;
        sort(s.begin(),s.end());	//排序使得相同元素相邻
        string path;
        bool* v=new bool[s.size()]{};
        dfs(s,res,path,v,0);
        return res;
    }
    void dfs(string& s,vector<string>& res,string& path,bool v[],int depth)
    {
        if(depth==s.size()){
            
                res.push_back(path);
            return;
        }
        for(int i=0;i<s.size();i++)
        {
            if(!v[i])
                {
                    if(i>0&&s[i]==s[i-1]&&v[i-1])	//如果这个元素和上一个一样,就不需要遍历了,
                    								//因为结果会和上一个元素的结果一样
                        continue;
                    v[i]=true;
                    path.push_back(s[i]);		
                    dfs(s,res,path,v,depth+1);
                    path.pop_back();		//状态重置,即回溯到之前的状态
                    v[i]=false;
                }
        }
    }
};

2.3 括号生成

https://leetcode-cn.com/problems/generate-parentheses/
在这里插入图片描述
思路分析:创建记录路径和结果的数组,左括号任意时刻都可以放,右括号只能在左括号放置的比右括号多的时候。每次递归出来后要回溯到之前的状态。

class Solution {
public:
    vector<string> generateParenthesis(int n) {
        vector<string> res;
        int l=n,r=n;	//初始左右括号的剩余数
        string path;	//记录左右括号的生成路径
        dfs(res,l,r,n,path);
        return res;
    }
    void dfs(vector<string>& res,int l,int r,int n,string& path)
    {
        if(path.size()==2*n){
            res.push_back(path);
            return;
        }
        
            if(l>0){
                path+='(';
                dfs(res,l-1,r,n,path);
                path.pop_back();
            }
            if(r>l){
                path+=')';
                dfs(res,l,r-1,n, path);
                path.pop_back();
            }
        
    }
};

2.4 矩阵中的路径

https://leetcode-cn.com/problems/ju-zhen-zhong-de-lu-jing-lcof/
在这里插入图片描述
思路分析:扫描矩阵中的各点,若与word的首字母一致则从这个点开始 dfs

class Solution {
public:
    bool exist(vector<vector<char>>& board, string word) {
        int row=board.size(),line=board[0].size();
       
        for(int i=0;i<row;i++)
        {
            for(int j=0;j<line;j++){
                if(board[i][j]==word[0]){
                    vector<vector<bool>> v(row,vector<bool>(line));
                    v[i][j]=true;
                    if(dfs(board,i,j,word,v,row,line,0))
                        return true;
                }
                    
            }
                
        }
        return false; 
        
    }
    bool dfs(vector<vector<char>>& board,int i,int j,string& word,vector<vector<bool>>& v,int row,int line,int p)
    {
        if(p==(word.size()-1)&&board[i][j]==word[p])
            return true;
        if(i+1<row&&!v[i+1][j]&&board[i+1][j]==word[p+1]){
            v[i+1][j]=true;
            if(dfs(board,i+1,j,word,v,row,line,p+1))
                return true;
            v[i+1][j]=false;
        }
        if(j+1<line&&!v[i][j+1]&&board[i][j+1]==word[p+1]){
            v[i][j+1]=true;
            if(dfs(board,i,j+1,word,v,row,line,p+1))
                return true;
            v[i][j+1]=false;
        }
        if(i-1>=0&&!v[i-1][j]&&board[i-1][j]==word[p+1]){
            v[i-1][j]=true;
            if(dfs(board,i-1,j,word,v,row,line,p+1))
                return true;
            v[i-1][j]=false;
        }
        if(j-1>=0&&!v[i][j-1]&&board[i][j-1]==word[p+1]){
            v[i][j-1]=true;
            if(dfs(board,i,j-1,word,v,row,line,p+1))
                return true;
            v[i][j-1]=false;
        }
        return false;
    }
};

2.5 剑指 把数字翻译成字符串

https://leetcode-cn.com/problems/ba-shu-zi-fan-yi-cheng-zi-fu-chuan-lcof/
在这里插入图片描述
思路分析:利用回溯的思想从左向右读取一位或两位数字,将其转换为字符加入string中,同时利用set不能有重复的特性去重,最终res内就是所有的可能排列,它的大小即为所有的翻译方法

class Solution {
public:
    int translateNum(int num) {
        set<string> res;
        string s;
        dfs(num,s,res);
        return res.size();
    }
    void dfs(int num,string& s,set<string>& res)
    {
        if(num==0){
            res.insert(s);
            return;
        }
        if((num%100)<26&&(num%100)>9){
            s+=num%100+'a';
            dfs(num/100,s,res);
            s.pop_back();
        }
        s+=num%10+'a';
        dfs(num/10,s,res);
        s.pop_back();
    }
};

3. 总结

  1. 回溯算法的思路是一种树形结构,每一次走到头就回退一步
  2. 回退一步必需要回溯到之前的状态,即必须要将状态重置
  3. 设置递归的终点,即树形图的叶子结点
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值