leetcode——回溯算法17、22、51、52、

前提:解可行可以用n元数组表示

(1)贪心法、动态规划法、回溯法区别

贪心法:最优子结构性质,最优量度标准,求最佳解

动态规划化:最优子结构性质,重叠子问题,求最佳解

回溯法(求可行解或者最优解):找到约束条件、目标函数(如果求最佳解),上下界函数(求最优需要)

广度优先遍历和深度优先遍历都可遍历状态空间树,深度优先遍历的叫回溯法,前者叫分枝限界法

深度优先遍历:深度优先搜索沿着HTML文件上的超链走到不能再深入为止,然后返回到某一个HTML文件,再继续选择该HTML文件中的其他超链。当不再有其他超链可选择时,具体可以查考这里。

此处选择回溯法,因为这几题都是求可行解,且可行解可以用数组表示。

(2)代码

17题:

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

给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。

示例:

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

class Solution {
public:
    vector letterCombinations(string digits) 
    {
      
    if (digits.empty()) return {};
        vector res;
        vector dict{"", "", "abc", "def", "ghi", "jkl", "mno", "pqrs", "tuv", "wxyz"};
        letterCombinationsDFS(digits, dict, 0, "", res);
        return res;
    }
    void letterCombinationsDFS(string& digits, vector& dict, int level, string out, vector& res) {
        if (level == digits.size()) {res.push_back(out); return;}
        string str = dict[digits[level] - '0'];//找出digits数组每个ASCII值对应的字符串,及其巧妙
        for (int i = 0; i < str.size(); ++i) {
            letterCombinationsDFS(digits, dict, level + 1, out + str[i], res);
        }
    }
};

说明:

1、vector和数组很像,但数组不能动态增加元素,而且值得注意的是vector下标操作不添加元素,下标只能获取已存在的元素。

vector只是定义一个没有任何内容的内存块,所以这就是他们的最大区别

2、回溯法管用的语法中含有约束条件这项,由于该题没有约束条件,所以直接递归

3、回溯法的递归可以通过状态空间树和深度遍历理解,容易些

具体参考这个

22题:

给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。

例如,给出 n = 3,生成结果为:

[
  "((()))",
  "(()())",
  "(())()",
  "()(())",
  "()()()"
]

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/generate-parentheses
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

最终结果必定是左括号3个,右括号3个,所以不能简单的递归,还有数量限制,就觉得挺麻烦的,所以这里定义两个变量 left 和 right 分别表示剩余左右括号的个数,就简单啦。

递归结束条件如果在某次递归时,左括号的个数大于右括号的个数,说明此时生成的字符串中右括号的个数大于左括号的个数,即会出现 ')(' 这样的非法串,所以这种情况直接返回,不继续处理。

class Solution {
public:
    vector generateParenthesis(int n) {
        vector res;
        generateParenthesisDFS(n, n, "", res);
        return res;
    }
    void generateParenthesisDFS(int left, int right, string out, vector &res) {
        if (left > right) return;
        if (left == 0 && right == 0) res.push_back(out);
        else {
            if (left > 0) generateParenthesisDFS(left - 1, right, out + '(', res);
            if (right > 0) generateParenthesisDFS(left, right - 1, out + ')', res);
        }
    }
};

 

51.52题

51:

n 皇后问题研究的是如何将 n 个皇后放置在 n×n 的棋盘上,并且使皇后彼此之间不能相互攻击。

上图为 8 皇后问题的一种解法。

给定一个整数 n,返回所有不同的 n 皇后问题的解决方案。

每一种解法包含一个明确的 n 皇后问题的棋子放置方案,该方案中 'Q' 和 '.' 分别代表了皇后和空位。

示例:

输入: 4
输出: [
 [".Q..",  // 解法 1
  "...Q",
  "Q...",
  "..Q."],

 ["..Q.",  // 解法 2
  "Q...",
  "...Q",
  ".Q.."]
]

代码:

class Solution {
public:
    vector> solveNQueens(int n) {
        vector> res;
        vector queens(n, string(n, '.'));
        helper(0, queens, res);
        return res;
    }
    void helper(int curRow, vector& queens, vector>& res) {
        int n = queens.size();
        if (curRow == n) {
            res.push_back(queens);
            return;
        }
        for (int i = 0; i < n; ++i) {
            if (isValid(queens, curRow, i)) {
                queens[curRow][i] = 'Q';
                helper(curRow + 1, queens, res);
                queens[curRow][i] = '.';//初始化,由于解queens存在数组,为了防止两个解的前后干扰
            }
        }
    }
    bool isValid(vector& queens, int row, int col)//约束条件
 {
        for (int i = 0; i < row; ++i) {
            if (queens[i][col] == 'Q') return false;
        }
        for (int i = row - 1, j = col - 1; i >= 0 && j >= 0; --i, --j) {
            if (queens[i][j] == 'Q') return false;
        }
        for (int i = row - 1, j = col + 1; i >= 0 && j < queens.size(); --i, ++j) {
            if (queens[i][j] == 'Q') return false;
        }
        return true;
    }
};

批注:

1、vecota[100] 与vector > 前者是100个一维动态数组, 即100个vector; 后者为二维动态数组, 即元素为vector的vector.

2、String(number, character)
String 函数的语法有下面的命名参数:
部分 说明
number 必要参数;Long。返回的字符串长度。如果 number 包含 Null,将返回 Null。
character 必要参数;Variant。为指定字符的字符码或字符串表达式,其第一个字符将用于建立返回的字符串。如果 character 包含 Null,就会返回 Null。

具体参考这个

51代码参考这个

52、代码参考这里,这个代码很巧秒的把解化成一维数组,pos[i]代表皇后i位于pos[i]列

总结

框图:

leetcode——回溯算法17、22、51、52、_第1张图片

共同点:

1、都用res做为可行解的集合,不同的是根据可行解的不同类型,若是一维数组,这用vecotres,如果是二维的用vector >

2、如果是一维可行解,用out当每次递归产生的可行解;如果是二维可行解,用一个二维数组queens当每次产生的可行解,用String(number, character)定义产生

3、lever控制每次递归的子选择;out控制可行解。lever和out个都可控制递归结束,如果是层数确定,则用lever;如果层数不定,用out.

不同点

1、如果是一维可行解,用out当每次递归产生的可行解,在每次遍历各个可行子选项时不改变out,out的改变是由于参数传递,因此不需要担心两个可行解之间相互影响Out;但若是二维可行解,因为不同用out+字符串表示加入子选项后的解状态,因此只能修改queens,故递归后需要恢复到原状态,保证各个子选择之间不相互影响。

2、约束条件的位置不太一样,这个影响不大,无论是放在判断是否为可行解的前面,还是遍历子选择的后面,意义都一样,即判断加入子选择后是否满足约束条件,不过第二种更好,直接免得无用的函数调用。

 

 

参考链接:

https://zhidao.baidu.com/question/651050559121168645.html

https://www.cnblogs.com/mzct123/p/5535723.html

https://blog.csdn.net/rominsoft/article/details/18195399

https://zhidao.baidu.com/question/208825587.html

你可能感兴趣的:(leetcode,leetcode,回溯法,n皇后问题)