回溯法和 深度优先搜索的区别与相同点:
相同点:
回溯法在实现上也是遵循深度优先的,即一步一步往前探索,而不像广度优先那样,由近及远一片一片地扫。
不同点:
(1)访问序
深度优先遍历:目的是“遍历”,本质是无序的。也就是说访问次序不重要,重要的是都被访问过了。可以参见题Surrounded Regions,深度优先只需要把从边界起始的’O’全部访问到即可。因此在实现上,只需要对于每个位置记录是否被visited就足够了。
回溯法: 目的是“求解过程”,本质是有序的。也就是说必须每一步都是要求的次序。可以参见题Word Search,需要以要求的序进行深度优先探索,必须每一步都符合要求。因此在实现上,不能使用visited记录,因为同样的内容不同的序访问就会造成不同的结果,而不是仅仅“是否被访问过”这么简单。要使用访问状态来记录,也就是对于每个点记录已经访问过的邻居方向,回溯之后从新的未访问过的方向去访问邻居。至于这点点之前有没有被访问过并不重要,重要的是没有以当前的序进行访问。
(2)访问次数
深度优先遍历:已经访问过的节点不再访问,所有点仅访问一次。
回溯法:已经访问过的点可能再次访问,也可能存在没有被访问过的点。
1、字符串的排列(剑指offer 38)
题目描述:
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。
输入描述:
输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。
解题思路:
- 字符串全排列问题,可以用回溯算法解决。
- 将字符串看成两部分,第一个字符与后面的所有字符,用第一个字符与后面的某一个字符交换并且递归直到到达字符串结尾,将该字符压入vector,并且将上一次交换的字符复位。
- 对vector 采用 sort unique erase的方法可以去除重复的元素,得到最终无重复的全部排列组合。
class Solution {
public:
void permutation(string str,int i,int n,vector<string>& res)
{
if(i==n)
res.push_back(str);
for(int j=i;j<n;++j)
{
swap(str[j],str[i]);
permutation(str,i+1,n,res);
swap(str[j],str[i]);
}
}
vector<string> Permutation(string str) {
if(str=="")
return {};
vector<string> res;
int n=str.size();
permutation(str,0,n,res);
sort(res.begin(),res.end());
res.erase(unique(res.begin(),res.end()),res.end());
return res;
}
};
2、分割回文串(leetcode 131)
解题思路: 深度优先搜索 (回溯法)
以下标i为每段字符串的终止为止,以index记录上一次字符串的结束位置,从index到i-index+1判断是否回文串,是回文串则继续判断i+1开始到n-1位置,重复之前的操作并且更新index为i+1 。当index>=n时表示此时分割的全都是回文串,将结果压入res中。否则回退到上一步,并将之前压入vec的一段字符串弹出,重新分割。
class Solution {
public:
bool isplindrome(string& s) //判断是否是回文串
{
int l=0;
int r=s.size()-1;
while(l<=r)
{
if(s[l]!=s[r])
return false;
l++;
r--;
}
return true;
}
void backtrack(vector<vector<string>>& res,vector<string>& vec,string& s,int index)
{
if(index>=s.size())
{
res.push_back(vec);
return;
}
int n=s.size();
for(int i=index;i<n;i++)
{
string str=s.substr(index,i-index+1); //判断当前分割的子串是否是回文串
if(isplindrome(str))
{
vec.push_back(str); //当前串是回文串,压入vec
backtrack(res,vec,s,i+1); //如果之前子串是回文串,从当前子串位置后面继续分割
vec.pop_back(); //回退到上一步,并弹出之前压入的子串
}
}
}
vector<vector<string>> partition(string s) {
vector<vector<string>> res;
vector<string> vec;
backtrack(res,vec,s,0);
return res;
}
};