前提:解可行可以用n元数组表示
贪心法:最优子结构性质,最优量度标准,求最佳解
动态规划化:最优子结构性质,重叠子问题,求最佳解
回溯法(求可行解或者最优解):找到约束条件、目标函数(如果求最佳解),上下界函数(求最优需要)
广度优先遍历和深度优先遍历都可遍历状态空间树,深度优先遍历的叫回溯法,前者叫分枝限界法
深度优先遍历:深度优先搜索沿着HTML文件上的超链走到不能再深入为止,然后返回到某一个HTML文件,再继续选择该HTML文件中的其他超链。当不再有其他超链可选择时,具体可以查考这里。
此处选择回溯法,因为这几题都是求可行解,且可行解可以用数组表示。
给定一个仅包含数字 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、回溯法的递归可以通过状态空间树和深度遍历理解,容易些
给出 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:
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、vecot
2、String(number, character)
String 函数的语法有下面的命名参数:
部分 说明
number 必要参数;Long。返回的字符串长度。如果 number 包含 Null,将返回 Null。
character 必要参数;Variant。为指定字符的字符码或字符串表达式,其第一个字符将用于建立返回的字符串。如果 character 包含 Null,就会返回 Null。
具体参考这个
51代码参考这个
52、代码参考这里,这个代码很巧秒的把解化成一维数组,pos[i]代表皇后i位于pos[i]列
框图:
共同点:
1、都用res做为可行解的集合,不同的是根据可行解的不同类型,若是一维数组,这用vecot
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