回溯法 或dfs 或深度优先 题总结

来源:互联网 发布:嵌入式c语言 编辑:程序博客网 时间:2024/06/05 02:50
回溯法总结
一道数字到字符串的题,将数字映射到手机的字符串上,是编程之美版本的简略版,简明的DFS,其实我更愿意用backtrack framework来做,说成是回溯法,本质没有区别
vector<string> numtoletter;void f(vector<char>& strvec, int selectn, int n,string digits, vector<string> numtoletter, vector<string>& allcombinations){if(selectn==n){string str;for(int i=0;i<n;i++)str+=strvec.at(i);allcombinations.push_back(str);}else{for(int i=0;i< numtoletter.at(digits.at(selectn)-'2').length();i++)// if 1?{strvec.push_back(numtoletter.at(digits.at(selectn)-'2').at(i));f(strvec,selectn+1,n,digits,numtoletter,allcombinations);strvec.pop_back();//f(strvec,selectn+1,n,digits,numtoletter,allcombinations);}}}vector<string> letterCombinations(string digits) {numtoletter.push_back("abc");numtoletter.push_back("def");numtoletter.push_back("ghi");numtoletter.push_back("jkl");numtoletter.push_back("mno");numtoletter.push_back("pqrs");numtoletter.push_back("tuv");numtoletter.push_back("wxyz");vector<char> strvec;vector<string> allcombinations;f(strvec,0,digits.size(),digits,numtoletter,allcombinations);return allcombinations;}




期间犯了一个错误,就是for里面多写了一个f(),结果导致out of memory等奇怪错误。 主要是受了candidate有两个情况,直接写成两个,外加用select bool数组将unmake 和下一轮的make重合在一起,于是下意识的多写了一个f() 而没有仔细思考。
其实一般框架都是一个for里面先逐个make candidate,然后递归,然后unmake


外加一道子集和问题,返回给定集合S 的所有子集,典型的回溯。其实感觉这个就是有模板的,但是如果情况复杂些,我还是改起来有点费劲= =
void subsets_recur(bool *select, vector<int> S, vector<vector<int>>&intvec, int selectn, int n){if(selectn==n){vector<int> vec;for(int i=0;i<S.size();i++){if(select[i]==true)vec.push_back(S.at(i));}intvec.push_back(vec);}else{select[selectn]=false;//not selectsubsets_recur(select,S,intvec,selectn+1,n);select[selectn]=true;subsets_recur(select,S,intvec,selectn+1,n);}}vector<vector<int> > subsets(vector<int> &S) {bool *select=new bool[S.size()];vector<vector<int>> intvec;sort(S.begin(),S.end());subsets_recur(select,S,intvec,0,S.size());delete[] select;return intvec;}


打印 C(n,k)的所有情况,里面多一个k,于是对应多一个selectk表示当前已经选了多少变量了,好像递归出口如果写成下面这个会有问题,
具体还没分析出为啥= =
if(selectk==k){string str;for(int i=0;i<n;i++)if(select[i]==true)str+='1';elsestr+='0';//cout<<endl;numset.push_back(str);}else if(selectn==n);else{select[selectn]=false;SubSetNum(numset,select,selectk,k,selectn+1,n);select[selectn]=true;SubSetNum(numset,select,selectk+1,k,selectn+1,n);}


C(n,k)代码
void Combinations(bool* select, vector<vector<int>> &vecset,int selectk, int k, int selectn, int n){if(k>n) return ;if(selectn==n){if(selectk==k)//proning{vector<int> vec;for(int i=0;i<n;i++){if(select[i]==true)vec.push_back(i+1);}vecset.push_back(vec);}}else{select[selectn]=false;Combinations(select, vecset,selectk,k,selectn+1,n);select[selectn]=true;Combinations(select, vecset,selectk+1,k,selectn+1,n);}}vector<vector<int> > combine(int n, int k){bool* select=new bool[n];memset(select,0,sizeof(bool)*n);vector<vector<int>> vecset;Combinations(select,vecset,0,k,0,n);delete [] select;return vecset;}





总结:回溯法其实就是你for循环要做,但是不知道多少层for循环的时候,或者是解指数(子集树)或者阶乘级(排列树),然后里面涉及的每个当前解的变量要么数组传递,递归调用共享访问,要么vector引用传递保持递归调用一致,要么全局变量。
然后记录全局变量的也是如此,保持全局一致,如果作为递归参数千万不要值传递,导致每个递归栈保存的都是一个copy而不是访问全局的一个。然后递归出口的时候一定要把情况罗列清楚,多个条件逻辑与或分析能力。


本质是for遍历每个数,make, recursive, unmake 不要多加recursive导致和  01选择的混淆

但是如果弄个有重复数字的全排列,我就搞不清了,还有重复数字的组合数= =


0 0
原创粉丝点击