先来复习一下回溯算法,摘自百度百科:
回溯算法实际上一个类似枚举的搜索尝试过程,主要是在搜索尝试过程中寻找问题的解,当发现已不满足求解条件时,就“回溯”返回,尝试别的路径。回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。许多复杂的,规模较大的问题都可以使用回溯法,有“通用解题方法”的美称。
说白了,跟剪枝差不多,直接开工~
Question:
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例:
输入:"23"
输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
说明:
尽管上面的答案是按字典序排列的,但是你可以任意选择答案输出的顺序。
Solution:
map mapletter;
vector letterCombinations(string digits) {
mapletter.insert(pair('2',"abc"));
mapletter.insert(pair('3',"def"));
mapletter.insert(pair('4',"ghi"));
mapletter.insert(pair('5',"jkl"));
mapletter.insert(pair('6',"mno"));
mapletter.insert(pair('7',"pqrs"));
mapletter.insert(pair('8',"tuv"));
mapletter.insert(pair('9',"wxyz"));
vector res;
string path;
if(digits.empty())return res;
letterdfs(res,digits,path);
return res;
}
void letterdfs(vector &res, string digits, string path)
{
int d = digits.size(), p = path.size();
if(p == d) {res.push_back(path); return;}
for(int i = 0; i < mapletter[digits[p]].size(); i++)
{
path.push_back(mapletter[digits[p]][i]);
letterdfs(res,digits,path);
path.pop_back();
}
}
在letterdfs中动态记录path,每次向前进一步调用新的回溯函数,再退回,遍历下一个字母。
Question :
给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。
例如,给出 n = 3,生成结果为:
[
"((()))",
"(()())",
"(())()",
"()(())",
"()()()"
]
Solution:
我第一次写的代码是超时的,因为的出来的结果有重复的现象,我筛选了非重复值,这直接让时间复杂度多了一个量级:
vector generateParenthesis(int n) {
vector res;
if(n == 0) return res;
string path;
parendfs(res,path,n,n,0);
return res;
}
void parendfs(vector &res, string path, int n, int left, int right)
{
if(path.size() == n * 2) {
vector::iterator it = find(res.begin(),res.end(),path);
if(it == res.end())
res.push_back(path);
return;
}
for(int i = 0; i < left; i++)
{
path.push_back('(');
parendfs(res,path,n, left - 1, right + 1);
path.pop_back();
}
for(int i = 0; i < right; i++)
{
path.push_back(')');
parendfs(res,path,n,left, right - 1);
path.pop_back();
}
}
那么为什么会出现重复呢?考虑n的2的情况,我输出的答案是["(())","(())","()()","(())","(())","()()"],为什么会出现五次重复呢,初始left为2,剩余两个左括号需要使用,发现这道题跟上道题是不同的,上道题的字母组合,需要遍历每个位置的三个数字,但是这道题不一样,使用for循环导致重复输出了目标结果,修改后就可以了:
vector generateParenthesis(int n) {
vector res;
if(n == 0) return res;
string path;
parendfs(res,path,n,n,0);
return res;
}
void parendfs(vector &res, string path, int n, int left, int right)
{
if(path.size() == n * 2) {
res.push_back(path);
return;
}
if(right > 0)
{
path.push_back(')');
parendfs(res,path,n,left, right - 1);
path.pop_back();
}
if(left > 0)
{
path.push_back('(');
parendfs(res,path,n, left - 1, right + 1);
}
}
Question:
给定一个没有重复数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
Solution:
vector> permute(vector& nums) {
vector> res;
vector path;
if(nums.size() == 0) return res;
numsdfs(res,path,nums);
return res;
}
void numsdfs(vector> &res, vector path, vector nums)
{
int p = path.size(), n = nums.size();
if(p == n){
res.push_back(path);
return;
}
for(int i = p; i < n; i++)
{
int temp = nums[p];
nums[p] = nums[i];
nums[i] = temp;
path.push_back(nums[p]);
numsdfs(res,path,nums);
path.pop_back();
}
}
Question:
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
Solution:
vector> subsets(vector& nums) {
vector> res;
vector path;
int pos = 0;
subsetsdfs(res,path,nums,pos);
return res;
}
void subsetsdfs(vector> &res, vector path, vector nums,int pos)
{
//cout << pos << endl;
if(pos == nums.size())
{
res.push_back(path);
return;
}
subsetsdfs(res,path,nums,pos + 1);
path.push_back(nums[pos]);
subsetsdfs(res,path,nums,pos + 1);
}
Question:
给定一个二维网格和一个单词,找出该单词是否存在于网格中。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
示例:
board =
[
['A','B','C','E'],
['S','F','C','S'],
['A','D','E','E']
]
给定 word = "ABCCED", 返回 true.
给定 word = "SEE", 返回 true.
给定 word = "ABCB", 返回 false.
Solution:
这道题有点像迷宫问题:
bool exist(vector>& board, string word) {
if(word.length() == 0) return true;
for(int i = 0; i < board.size(); i++)
{
for(int j = 0; j < board[0].size(); j++)
{
if(word[0] == board[i][j])
if(worddfs(board,word,i,j,0)) return true;
}
}
return false;
}
bool worddfs(vector> board, string word, int i, int j, int pos)
{
if(word[pos] == board[i][j])
{
board[i][j] = '0';
pos++;
if(pos == word.size()) return true;
if(i > 0 && board[i-1][j] != '0')
if(worddfs(board,word,i-1,j,pos)) return true;
if(j > 0 && board[i][j-1] != '0')
if(worddfs(board,word,i,j-1,pos)) return true;
if(i < board.size() - 1&& board[i+1][j] != '0')
if(worddfs(board,word,i+1,j,pos)) return true;
if(j < board[0].size() - 1 && board[i][j+1] != '0')
if(worddfs(board,word,i,j+1,pos)) return true;
}
return false;
}
回溯法应该是最简单的一种算法了,记得之前老师说回溯法是最笨的方法,所以这个以后还是少用。