总结:回溯算法实际上是一个类似枚举的搜索尝试过程,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,其特点就是走不通就退回再选另一条路走。 在包含问题的所有解的解空间树中,按照深度优先搜索的策略,从根结点出发深度探索解空间树。当探索到某一结点时,要先判断该结点是否包含问题的解,如果包含,就从该结点出发继续探索下去,如果该结点不包含问题的解,则逐层向其祖先结点回溯。在使用回溯法的时候要注意一点,一定要设置一个向前搜索的终结条件,否则函数就会无限向前搜索从而无法“退回”重新选择。
题目1:电话号码的字母组合
给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。
给出数字到字母的映射如下(与电话按键相同)。注意 1 不对应任何字母。
示例:
输入:"23"
输出:["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"].
思路:这个问题实质上就是不同数组间元素的排列组合。用一个map记录不同数字对应的字母集,然后根据所给出的字符串中的数字找到相对应的字母集,最后将各个字母集中的字母排列组合即可。
//用来存储键盘个数字所对应的字母
map<char,vector<char>> board;
//res是结果集,len是字符串长度,count是子集的长度
void helper(vector<string> &res,string digits,int len,string s,int count)
{
if(count==len)
{
res.push_back(s);
return;
}
else
{
vector<char> temp = board[digits[count]];
int temp_len = temp.size();
for(int i=0;i<temp_len;i++)
helper(res,digits,len,s+temp[i],count+1);
}
}
vector<string> letterCombinations(string digits)
{
vector<string> res;
int len = digits.size();
if(len==0)
return res;
board['2'] = {'a','b','c'};
board['3'] = {'d','e','f'};
board['4'] = {'g','h','i'};
board['5'] = {'j','k','l'};
board['6'] = {'m','n','o'};
board['7'] = {'p','q','r','s'};
board['8'] = {'t','u','v'};
board['9'] = {'w','x','y','z'};
helper(res,digits,len,"",0);
return res;
}
题目2:生成括号
给出 n 代表生成括号的对数,请你写出一个函数,使其能够生成所有可能的并且有效的括号组合。
例如,给出 n = 3,生成结果为:
[
"((()))",
"(()())",
"(())()",
"()(())",
"()()()"
]
思路:每次在字符串中插入新字符有两种选择:一.如果插入的"("没有到达规定数目,可以继续插入"(";二.如果插入的"("数量大于插入的")"的数量可以插入")"。我们只需要在每次插入的时候进行判断,并且根据所判断的结果向下进行递归就可以得到所有情况。
//rCount代表已经放入字符串的'('的数量,lCount代表已经放入的')'的数量
void helper(vector<string> &res,string s,int n,int rCount,int lCount)
{
if(rCount==n&&lCount==n)
{
res.push_back(s);
return;
}
else
{
if(rCount<n)
helper(res,s+"(",n,rCount+1,lCount);
//放入")"数量必须小于放入"("数量
if(lCount<rCount)
helper(res,s+")",n,rCount,lCount+1);
}
}
vector<string> generateParenthesis(int n)
{
vector<string> result;
if(n==0)
return result;
helper(result,"",n,0,0);
return result;
}
题目3:全排列
给定一个没有重复数字的序列,返回其所有可能的全排列。
示例:
输入: [1,2,3]
输出:
[
[1,2,3],
[1,3,2],
[2,1,3],
[2,3,1],
[3,1,2],
[3,2,1]
]
思路:全排列实质上就是nums[i]与nums[j](j>=i)依次进行互换所得到的新的数组。
void swap(int &num1,int &num2)
{
int temp = num1;
num1 = num2;
num2 = temp;
}
void solve(vector<vector<int> > &result,vector<int> &nums,int len,int count)
{
if(count==len)
{
result.push_back(nums);
return;
}
else
{
for(int i=count;i<len;i++)
{
swap(nums[count],nums[i]);
solve(result,nums,len,count+1);
//这里如果不换回来后面会产生很多重复的数组
swap(nums[count],nums[i]);
}
}
}
vector<vector<int> > permute(vector<int> &nums)
{
vector<vector<int> > result;
int len = nums.size();
solve(result,nums,len,0);
return result;
}
题目4:子集
给定一组不含重复元素的整数数组 nums,返回该数组所有可能的子集(幂集)。
说明:解集不能包含重复的子集。
示例:
输入: nums = [1,2,3]
输出:
[
[3],
[1],
[2],
[1,2,3],
[1,3],
[2,3],
[1,2],
[]
]
思路:得到一个数组的所有子集实质上就是从数组中抽取长度为i(0=<i<=len)的所有子集。
//solve函数是用于从nums中抽取长度为aim的子集,index是为了保证抽取的数字在上一次抽取数字的后面,保证不重复
void solve(vector<vector<int> > &result,vector<int> &subset,vector<int> nums,int len,int aim,int index,int count)
{
if(count==aim)
{
result.push_back(subset);
return;
}
else
{
for(int i=index;i<len;i++)
{
subset.push_back(nums[i]);
solve(result,subset,nums,len,aim,i+1,count+1);
subset.pop_back();
}
return;
}
}
vector<vector<int> > subsets(vector<int>& nums)
{
vector<vector<int> > result;
int len = nums.size();
if(len==0)
return result;
//抽取长度不同的子集
for(int i=0;i<=len;i++)
{
vector<int> subset;
solve(result,subset,nums,len,i,0,0);
}
return result;
}
题目5:单词搜索
给定一个二维网格和一个单词,找出该单词是否存在于网格中。
单词必须按照字母顺序,通过相邻的单元格内的字母构成,其中“相邻”单元格是那些水平相邻或垂直相邻的单元格。同一个单元格内的字母不允许被重复使用。
示例:
board =
[
['A','B','C','E'],
['S','F','C','S'],
['A','D','E','E']
]
给定 word = "ABCCED", 返回 true.
给定 word = "SEE", 返回 true.
给定 word = "ABCB", 返回 false.
思路:遍历二维数组,当遍历到和给定字符串首字母相同的字母时,向其上下左右四个方向搜索,寻找是否有与给定字符串第二个字母相同的字母与其相连,如果有则重复上述过程,直到找到全部字符串,如果没有找到全部字符串则继续向后遍历二维数组。要注意的是遍历过的字符要做标记,否则会产生重复遍历的情况。
//rlen和clen是二维网格的宽和长,len是字符串的长度,x和y是字符的位置
bool judge(vector<vector<char> > &board,int rlen,int clen,string word,int len,int count,int x,int y)
{
if(count==len)
return true;
else
{
char temp = board[x][y];
//为了避免重复遍历
board[x][y] = '#';
if(x>0&&board[x-1][y]==word[count]&&judge(board,rlen,clen,word,len,count+1,x-1,y))
return true;
if(x<rlen-1&&board[x+1][y]==word[count]&&judge(board,rlen,clen,word,len,count+1,x+1,y))
return true;
if(y>0&&board[x][y-1]==word[count]&&judge(board,rlen,clen,word,len,count+1,x,y-1))
return true;
if(y<clen-1&&board[x][y+1]==word[count]&&judge(board,rlen,clen,word,len,count+1,x,y+1))
return true;
board[x][y] = temp;
return false;
}
}
bool exist(vector<vector<char> > &board, string word)
{
int rlen = board.size();
int clen = board[0].size();
int len = word.size();
if(rlen==0||clen==0||len==0)
return false;
for(int i=0;i<rlen;i++)
{
for(int j=0;j<clen;j++)
{
if(board[i][j]==word[0])
{
if(judge(board,rlen,clen,word,len,1,i,j))
return true;
}
}
}
return false;
}