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

回溯法总结
一道数字到字符串的题,将数字映射到手机的字符串上,是编程之美版本的简略版,简明的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 select
		subsets_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';
			else
				str+='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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值