搜索算法之回溯

回溯是一种通过穷举所有可能情况来找到所有解的算法。如果一个候选解最后被发现并不是可行解,回溯算法会舍弃
它,并在前面的一些步骤做出一些修改,并重新尝试找到可行解。回溯算法一般会结合在搜索算法中。

1. 电话号码的字母组合

给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
在这里插入图片描述
示例:

输入:“23”
输出:[“ad”, “ae”, “af”, “bd”, “be”, “bf”, “cd”, “ce”, “cf”].

来源:力扣(LeetCode)

解析:
找到当前数字对应的字符串映射;
用之前的凭借的字符串继续拼接当前映射中的每一个字符;
最后一次,保存组合。

class Solution {
public:
    void DFS(int depth, string digits,string curStr,vector<string>& stringArr)
    {
        static unordered_map<char,string> digitsMap={{'2',"abc"},{'3',"def"},{'4',"ghi"},{'5',"jkl"},{'6',"mno"},{'7',"pqrs"},{'8',"tuv"},{'9',"wxyz"}};
        if(depth == digits.size())
        {
            stringArr.push_back(curStr);
            return;
        }
        string digitMap=digitsMap[digits[depth]];
        //digits[depth]
        for(auto& e : digitMap)
        {
            DFS(depth+1,digits,curStr+e,stringArr);
        }
    }
    vector<string> letterCombinations(string digits) {
        vector<string> stringArr;
        if(digits.empty())
            return stringArr;
        DFS(0,digits,"",stringArr);
        return stringArr;
    }
};

2. 组合总数

给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。

candidates 中的数字可以无限制重复被选取。

说明:

所有数字(包括 target)都是正整数。
解集不能包含重复的组合。
示例 1:

输入: candidates = [2,3,6,7], target = 7,
所求解集为:
[
[7],
[2,2,3]
]

来源:力扣(LeetCode)
解析:
从最后一个累加的元素位置继续累加,避免重复
直到累加和大于等于目标值的时候,回退。如果等于目标,保存结果

class Solution {
public:
    void DFS(vector<vector<int>>& Mat, vector<int>& Arr, int target, int curSum, vector<int>& candidates, int prePos)
    {
        if(curSum>=target)
        {
            if(curSum==target)
            {
                Mat.push_back(Arr);                
            }
            return;
        }
        for(int i = prePos; i<candidates.size(); i++)
        {
            Arr.push_back(candidates[i]);
            DFS(Mat,Arr,target,curSum+candidates[i],candidates,i);
            Arr.pop_back();
        }
    }
    vector<vector<int>> combinationSum(vector<int>& candidates, int target) {
        vector<int> Arr;
        vector<vector<int>> Mat;
        DFS(Mat,Arr,target,0,candidates,0);
        return Mat;
    }
};

3. N皇后

n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。(不能摆放在同一行,同一列,同一斜线)
给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。

每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 ‘Q’ 和 ‘.’ 分别代表了皇后和空位。
来源:力扣(LeetCode)

解析:
尝试当前行的每一列,判断当前位置是否和已经确定的皇后位置冲突,
不冲突:保存位置,继续确定下一行,
冲突:回溯,尝试其他位置

同行:行相同,但是不会同行
同列:列相同
正斜线:坐标差相同
反斜线:坐标和相同

class Solution {
public:
    bool IsValid(vector<pair<int,int>>& sigleMethod,int row,int col)
    {
        for(pair<int,int> &i : sigleMethod)
        {
            //同一列 ,同一反斜线,同一斜线
            if(i.second == col || i.first+i.second == row+col || i.first-i.second == row-col)
                return false;
        }
        return true;
    }
    void DFS(vector<vector<pair<int,int>>>& allMethod, vector<pair<int,int>>& sigleMethod, int n, int curRow)
    {
        if(curRow == n)
            allMethod.push_back(sigleMethod);
        //尝试当前行的每一个位置
        for(int col = 0; col < n; ++col)
        {
            if(IsValid(sigleMethod,curRow,col))
            {
                sigleMethod.emplace_back(curRow,col);
                DFS(allMethod,sigleMethod,n,curRow+1);
                sigleMethod.pop_back();//回溯
            }
        }
        
    }
    vector<vector<string>> solveNQueens(int n) {
        vector<vector<string>> finalRet;
        vector<vector<pair<int,int>>> allMethod;
        vector<pair<int,int>> sigleMethod;
        DFS(allMethod,sigleMethod,n,0);
        //转换结果,把所有的方法转换成字符串
        //遍历每一种方法
        for(auto& curMethod : allMethod)
        {
            vector<string> stringMethod(n,string(n,'.'));
            //把皇后的位置改为Q
            for(auto& curPos:curMethod)
            {
                stringMethod[curPos.first][curPos.second]='Q';
            }
            finalRet.push_back(stringMethod);
        }
        return finalRet;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值