用STL实现DFS/BFS算法
——检查重复状态
前几天,有网友留言说我使用“先深”、“先宽”的说法是狗屁二流子词汇,正确的说法应该是“深度优先”,“广度优先”。态度和用词虽然有点让人不好接受,但提的意见终归是有点道理的。请实话,我已经不记得最早是从哪里学来的“先深”、“先宽”,但可以肯定一点,本人是发明不出这样的词汇的,只是认为这两个词还不错,比较简单明了,所以就用上了。后来,我上GOOGLE查了一下,结果发现的确用四个字的比用两个字多得多,看来多数人还是喜欢长一点的词,或者说长一点的那两个词更通用一些。于是我就想是不是该“改正”一下自己的“错误”,换个大家更容易接受的词用用。
接下来,很高兴地看到有网友挺“先深”、“先宽”,于是我又想,如果我这就换了,岂不是有点对不起那些和我一样喜欢用“先深”、“先宽”的网友?思前想后,干脆用E文好了,所以就有了这个新的标题。
转入正题,前面我们说过,到目前为止我们的DFS/BFS算法还只能搜索无重复状态的状态空间树,而象下象棋、推箱子等问题,都会出现问题状态在几步之后可能会回到以前出现过的状态的情况。当重复状态出现时,我们必须能够识别出来,并且不应把它放入搜索树中。最简单、直接的做法就是,当问题状态类的nextStep()成员函数返回一组下一步可能状态时,我们对这些状态逐一进行检查,如果发现有重复的,就不把重复状态放入搜索树(即不入栈或入队列)。至于如何检查重复状态,我们还是采用函数模板最常见的方法,让使用者自己提供检查的方法,在我们的DFS/BFS算法中用一个泛型的模板参数来指定它。这样一来,我们的算法会变为这样(以BFS为例):
template <class T1, class T2, class T3 >
int BreadthFirstSearch(const T1& initState, const T2& afterFindSolution,
T3& checkDup)
//
前两个参数与旧版本相同
// checkDup :
仿函式,对于每一个下一步可能的状态调用之,它接受一个const T1&,
//
并返回一个Boolean值,true表示状态重复,false表示状态不重复
// return :
找到的答案数量
{
int n = 0;
queue<T1> states;
states.push(initState);
vector<T1> nextStates;
bool stop = false;
while (!stop && !states.empty())
{
T1 s = states.front();
states.pop();
nextStates.clear();
s.nextStep(nextStates);
for (typename vector<T1>::iterator i = nextStates.begin();
i != nextStates.end(); ++i)
{
if (i->isTarget())
{ //
找到一个目标状态
++n;
if (afterFindSolution(*i)) //
处理结果并决定是否停止搜索
{
stop = true;
break;
}
} else { //
不是目标状态,判断是否放入搜索队列中
if (!checkDup(*i)) //