在我的第一版代码中关于我的初版DFS代码我有一定的疑问,并且在其中有关于疑问的具体说明,这几天突然想到了解决方案就马上开始实践结果能够成功跑出答案因此对原版的代码进行更新。
先对思路进行说明:
第一版DFS代码中主要的疑问如下:
假如0移动到target为目标状态,那么容易得到路径①为正确最短路径,但是在代码中初始0节点扩展时可能并不是向着下扩展,而是先向右扩展,那么会首先走向路径②,此时会深度优先会优先扩展路径②,这个时候假如沿着路径②走会产生如下错误:
假设路径②走到第三步,也就是target节点的前一个节点就结束了,此时range刚好达到最大,这个时候路径②的深度优先就会被终止,进行其他路径的尝试,但是这个时候会产生一个巨大的问题,target的上一个节点已经被走过了,也就是说路径①走不了,因为访问当前节点是会先访问vis数组看当前节点是否被访问过,这个节点已经被访问了,所以路径②没有结果,路径①走不了。最后没有任何答案输出。
这个问题的本质就是一个递归回溯的问题,也就是说对于整个图(虽然我们研究的八数码问题,但是这里我们把他抽象为一个迷宫路径问题来看),我们把已经走过的路径的VIS都赋值为1(在八数码中就是把已经产生过的状态赋值为1),这里我们假设[1,1]为起点,且已经vis[1,1]=1;此时我们将路径②扩展,如前文状态所述把路径②走过的点全部vis置1,当然在target之前一个点,因为到达深度,DFS停止,函数return,没有找到结果,然后开始查找路径①,这个时候如果按照开始的设计思路,路径①上的节点因为被走过了无法扩展所以不会产生答案,最终就得不到答案。
因为我们这里就用到递归回溯。具体用代码举例:
核心代码在185,186行,就是在当前节点扩展成功后,进入下一个DFS,下一个DFS继续深度搜索,但是如果下一个DFS找不到结果,退出的时候就要在DFS之后加上一个antiVisit来把当前已经访问过的节点重新定义为没有访问过,方便其他状态从这个节点开始扩展。
这样使用递归形式的查找方式而不是while形式的查找可以让DFS算法成功使用,因此纠正第一版中的关于DFS和BFS的说法,两者不仅仅是在openTable表放的位置不同这么简单而已,两者差异还是很大的。
下面贴运行结果和源代码(注意我的代码任然是有些bug的,有一些情况的DFS判断是出不了答案的,并且其实前文中的问题①我是没有解决的,就是把所以跑出来的路径全部记录下来然后找到最短的才是最优结果,这里代码放出来仅仅是参考了):
#include<ctime>
#include"DFS.h"
int main() {
clock_t start, finish;
start = clock();
initVisit();
cout << "当前状态:724506831" << endl;
cout << "目标状态:012345678" << endl;
Node originNode;
originNode.code = origin;
originNode.depth = 0;
originNode.parent = NULL;
checkVisit(originNode);
openTable.push_front(originNode);
dfs();
finish = clock();
cout << (finish - start) << "毫秒" << endl;
return 0;
}
#pragma once
#include<iostream>
#include<queue>
#include<list>
#include<cstring>
using namespace std;
const int maxn = 400000;
const int limit = 26;
const string origin = "724506831";
const string goal = "012345678";// 204756831 254736801
int vis[maxn], hashString[9];
struct Node {
string code;
struct Node* parent;
int depth;
};
Node move(Node temp, int x) {
int loc = -1;
for (int i = 0; i < 9; i++) {
if (temp.code[i] == '0') {
loc = i;
}
}
temp.depth++;
if (x == 1) {//上移
if (loc >= 3) {
char tempString = temp.code[loc];//找到空格
temp.code[loc] = temp.code[loc - 3];
temp.code[loc - 3] = tempString;
}
else {
temp.code = "";
}
}
else if (x == 2) {//下移
if (loc <= 5) {
char tempString = temp.code[loc];//找到空格
temp.code[loc] = temp.code[loc + 3];
temp.code[loc + 3] = tempString;
}
else {
temp.code = "";
}
}
else if (x == 3) {//左移
if (loc != 0 && loc != 3 && loc != 6) {
char tempString = temp.code[loc];//找到空格
temp.code[loc] = temp.code[loc - 1];
temp.code[loc - 1] = tempString;
}
else {
temp.code = "";
}
}
else if (x == 4) {//右移
if (loc != 2 && loc != 5 && loc != 8) {
char tempString = temp.code[loc];//找到空格
temp.code[loc] = temp.code[loc + 1];
temp.code[loc + 1] = tempString;
}
else {
temp.code = "";
}
}
return temp;
}
bool isGoal(Node temp) {//判断是否为最终的目标节点
if (temp.code == goal) {
return true;
}
return false;
}
void show(string temp) {//输出当前字符串
if (!temp.empty()) {
cout << temp[0] << " " << temp[1] << " " << temp[2] << endl
<< temp[3] << " " << temp[4] << " " << temp[5] << endl
<< temp[6] << " " << temp[7] << " " << temp[8] << endl << endl;
}
else {
cout << "空的" << endl;
}
}
//路径
deque<Node> findPath(Node ansNode) {
deque<Node> dequeList;
Node temp = ansNode;
while (temp.code!=origin) {
dequeList.push_back(temp);
temp = *temp.parent;
}
dequeList.push_back(temp);
//cout << dequeList.size() << endl;
return dequeList;
}
void showAnswer(Node ansNode) {
deque<Node> way(findPath(ansNode));
cout << "共需要" << way.size()-1 << "步" << endl;
for (int i = way.size() - 1; i >= 0; i--) {
show(way.at(i).code);
}
//输出目标
}
void initVisit() {
hashString[0] = 1;
for (int i = 1; i < 9; ++i) hashString[i] = hashString[i - 1] * i;
memset(vis, 0, sizeof(0));
}
bool checkVisit(Node temp) {
int nowString[9];
int code = 0;
for (int i = 0; i < 9; i++) {
nowString[i] = temp.code[i] - '0';
}
for (int i = 0; i < 9; i++) {
int cnt = 0;
for (int j = i + 1; j < 9; ++j) if (nowString[j] < nowString[i]) cnt++;
code += hashString[8 - i] * cnt;
}
if (vis[code]) return true;
else {
vis[code] = 1;
return false;
}
}
void antiVisit(Node temp) {
int nowString[9];
int code = 0;
for (int i = 0; i < 9; i++) {
nowString[i] = temp.code[i] - '0';
}
for (int i = 0; i < 9; i++) {
int cnt = 0;
for (int j = i + 1; j < 9; ++j) if (nowString[j] < nowString[i]) cnt++;
code += hashString[8 - i] * cnt;
}
if (vis[code]) vis[code] = 0;
}
deque<Node> openTable, closeTable;
void dfs() {
if (openTable.empty()) {
cout << "no answer" << endl;
return;
}
Node tempNode = openTable.front();
openTable.pop_front();
for (int j = 1; j <= 4; j++) {
Node secTempNode = move(tempNode, j);//扩展节点n
secTempNode.parent = NULL;
if (secTempNode.code == "") {
continue;
}
if (checkVisit(secTempNode)) {
continue;
}
//节点n的深度是否等于深度界限
if (secTempNode.depth > limit) {
return;
}
//到这里扩展的节点确定合法
secTempNode.parent = &tempNode;
if (isGoal(secTempNode)) {//是否有任何后继节点为目标节点
cout << "DFS找到结果:" << endl;
showAnswer(secTempNode);
antiVisit(secTempNode);//解除目标点的VIS方便其他状态也能到达目标点
return;
}
openTable.push_front(secTempNode); //把其后裔节点放入OPEN表的前端
dfs();
antiVisit(secTempNode);
}
}//end of dfs