回溯法和树的遍历

     以下内容主要参考了严蔚敏版的数据结构教材。
     回溯法是求解某些问题的全部或部分解的通用算法,特别是带有限制条件的问题。它通过不断的产生问题的完整解的片段并不断增长完整解的片段来获取该问题的完整解。当发现某个完整解的片段不能生成最后的完整解时,就会丢弃所有以该片段解为基础的完整解。
     回溯法会首先列出完整解的片段的一个集合。这个集合原则上可以通过不同的方式最后给出给定的问题的所有解。这是通过不断地对该完整解的片段的集合中的片段解不断扩展而达到的。
     给出一个简单的例子。假设现在需要求集合 A = { 1 , 2 , 3 } A=\{1,2,3\} A={1,2,3}的幂集。集合A的幂集是由集合A的所有子集组成的集合。因此集合A的幂集中的元素是一个集合,它或者是空集,或者含有集合A中的一个元素或者含有集合A中的l两个元素或者含有集合A中的三个元素。从集合A中的每一个元素来看,它要么属于幂集中每一个集合或者不属于。求幂集中的每一个集合可以看成是依次对集合A中的每一个元素“取”或者“舍”的过程。如图1所示。在图1中除叶子节点之外的所有节点都可以看成是该问题完整解的片段,通过不断取舍集合A中的元素来扩展该问题完整解的片段,当取舍完集合A中的所有元素时即得到了一个该问题的完整解。
     由此可看出求解问题的过程形成了一颗二叉树,求解过程即为从根节点递归的先根遍历该二叉树。回溯法也是设计递归过程的一种重要方法,它的求解过程实质上是一个先根遍历一颗“状态树”的过程,只不过这棵树不是遍历前预先建立的,而是隐含在遍历过程中。

 
图1.
void printSet(vector<int> set)
{
	for (int i = 0; i < set.size(); i++)
	{
		cout << set[i] << " ";
	}
	cout << endl;
}

void getPowerSet(int seTAElementIndex,vector<int> seTA, vector<int> &seTB)
{
	int currentElement;
	if (seTAElementIndex > seTA.size())
	{
		printSet(seTB);
	}
	else
	{
		currentElement = seTA[seTAElementIndex - 1];
		seTB.push_back(currentElement);
		getPowerSet(seTAElementIndex + 1, seTA, seTB);
		seTB.pop_back();
		getPowerSet(seTAElementIndex + 1, seTA, seTB);
	}
}
//测试程序
int main()
{
	int seTAElementIndex = 1;
	vector<int> seTA;
	seTA.push_back(1);
	seTA.push_back(2);
	seTA.push_back(3);
	vector<int> seTB;
	getPowerSet(seTAElementIndex, seTA, seTB);
}

     上面的例子是一个特例。有些其他问题比如八皇后问题的求解过程的状态树不是一颗满的多叉树。当求解过程中的完整解的片段和问题所求解产生矛盾时(即该完整解的片段不能生成最后的完整解),不再继续试探下去,这时出现的叶子节点不是该问题的完整解。这类问题的求解过程可以看成是在约束条件下进行先根遍历,并在遍历过程中删去那些不满足条件的分支。图2是4皇后问题的状态树的部分。图中打红叉的是被删去的分支。

 
图2.
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

qqssss121dfd

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值