【刷题】day3_leetcode回溯练习(46. 全排列+47. 全排列 II+17. 电话号码的字母组合+51. N皇后)

1.全排列

给定一个 没有重复 数字的序列,返回其所有可能的全排列。
示例:

输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]

class Solution {
public:
    vector<vector<int>> permute(vector<int>& nums) {
        vector<int> path;
        vector<vector<int>>res;
        dfs(path,res,nums);
        return res; 
    }
    void dfs(vector<int> &path,vector<vector<int>>&res,vector<int>& nums){
        if(path.size()==nums.size())
            res.push_back(path);
        for(int i=0;i<nums.size();i++){
            vector<int>::iterator it = find(path.begin(),path.end(),nums[i]);
            if(it==path.end()){
                path.push_back(nums[i]);
                dfs(path,res,nums);
                path.pop_back();
            }
        }
    }
};

2.全排列二

给定一个可包含重复数字的序列,返回所有不重复的全排列。
示例:
输入: [1,1,2]
输出:
[
[1,1,2],
[1,2,1],
[2,1,1]
]

分析:这题和上一个题的区别在于,出现了重复数字,如果每次的搜索空间还是整个数组,那重复数字会导致重复结果,如图所示,【1(1),1(2),2】和【1(2),1(1),1】其实是一种情况。所以为了避免重复,搜索空间只能是不重复的数字组成,而同时又要考虑到一条路径上可以出现重复,所以使用hash,hash的键值就是搜索空间,value不为0的就可以添加到路径中。
在这里插入图片描述

class Solution {
public:
    vector<vector<int>> permuteUnique(vector<int>& nums) {
        vector<vector<int>>res;
        vector<int>path;
        map<int,int>wide;
        for(int i=0;i<nums.size();i++){
            wide[nums[i]]++;
        } 
        dfs(res,path,nums,wide);
        return res;
    }
    void dfs(vector<vector<int>>&res,vector<int>&path,vector<int> nums,map<int,int>&wide){
        if(path.size()==nums.size())
            res.push_back(path);
        map<int,int>::iterator it;
        for(it=wide.begin();it!=wide.end();it++){
            if(it->second!=0){
                path.push_back(it->first);
                it->second--;
                dfs(res,path,nums,wide);
                it->second++;
                path.pop_back();
            }
        }
    }
};

3.电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例:
输入:“23”
输出:[“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”].
在这里插入图片描述

分析:搜索空间是数字对应的字母。这里的回溯要同时回溯路径和当前层数cur。

class Solution {
public:
    vector<string> letterCombinations(string digits) {
        
        map<int,string>mp; 
        vector<string> res;
        string path="";
        string list="abcdefghijklmnopqrstuvwxyz";
        if(digits=="")
            return res;
        int ind = 0;
        for(int i=2;i<7;i++){
            mp[i]+=list.substr(ind,ind+3);
            ind+=3;
        }
        mp[7]= "pqrs";
        mp[8]= "tuv";
        mp[9] = "wxyz";
       
        int cur = 0;//记录层数
        dfs(cur,res,path,digits,mp);
        return res;
    }
    void dfs(int cur,vector<string> &res,string path,string digits,map<int,string>mp){
        if(cur==digits.size()){
            res.push_back(path);
            return;
        }
        int ind = digits[cur]-'0';//得到map的key
        int end=3;
        if(ind ==7||ind==9) end=4;
        for(int i=0;i<end;i++){
            path+=mp[ind][i];
            dfs(++cur,res,path,digits,mp);
            cur--;
            path.pop_back();
        }
    }
};

4.N皇后

给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。
每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
示例:
输入: 4
输出: [
[".Q…", // 解法 1
“…Q”,
“Q…”,
“…Q.”],
["…Q.", // 解法 2
“Q…”,
“…Q”,
“.Q…”]
]
解释: 4 皇后问题存在两个不同的解法。

n皇后问题就是在n*n的棋盘上放置n个皇后,要求所有皇后不在同一行、列、对角线上。
这个题的复杂之处在于,想要在当前位置放置皇后需要判断这个位置同一行、列、对角线上是否已经有皇后了。
这个问题里搜索树的层数就是棋盘当前的行数,搜索空间是棋盘的列数,只需要考虑当前行之前放置的皇后对这次放置的影响(因为之后的行还未放置)。

class Solution {
public:
    /*每个皇后放置的行列斜对角线都不能重复*/
    /*
        层数:行数
        搜索空间:列数
        找到解的条件:当前层数已经到达n

    */
    vector<vector<string>> solveNQueens(int n) {
        vector<vector<string>> res;
        vector<string> path;
        for(int i=0;i<n;i++){
            string tmp = "";
            for(int j=0;j<n;j++)
                tmp+=".";
            path.push_back(tmp);
        }
        int row =0;
        dfs(res,path,row,n);
        return res;
    }
    bool isValid(int col,int row,vector<string> &path,int n){
        //检查同一行上是否有皇后
        for(int i=0;i<row;i++){
            if(path[i][col]=='Q')
                return false;
        }
        //检查主对角线上是否有皇后
        for(int i=row-1,j=col-1;i>=0&&j>=0;i--,j--){
            if(path[i][j]=='Q')
                return false;
        }
        //检查副对角线上是否有皇后
        for(int i=row-1,j=col+1;i>=0&&j<n;i--,j++){
            if(path[i][j]=='Q')
                return false;
        }
        return true;
    }
    void dfs(vector<vector<string>> &res,vector<string> &path,int row,int n){
        if(row==n){
            res.push_back(path);
            return;
        }
        for(int i=0;i<n;i++){
            path[row][i]='Q';
            if(isValid(i,row,path,n)){
                row++;
                dfs(res,path,row,n);
                row--;
            }
            path[row][i]='.';//注意这句要写在if外面,因为不管有没有往下搜索都已经放置皇后了,必须撤销操作
        }
    }
};

5.总结
回溯法框架:

result = []
def backtrack(路径, 选择列表):
    if 满足结束条件:
        result.add(路径)
        return
    
    for 选择 in 选择列表:
        做选择
        backtrack(路径, 选择列表)
        撤销选择

实质是多叉树的遍历问题。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值