老师要求写一下八数码问题的三种解决方法,,,哎真的是苦了孩子了,只有在网上找找大佬们的代码来扩展了一下思路才勉强写出来,这里也分享一下自己的理解。
宽度优先与深度优先算法在八数码问题上的主要操作大同小异,主要的差别在于,带扩展节点扩展之后放在对列的前后的区别,宽度优先放在表后,保证一层一层递进式的处理。深度优先放在表前,保证当前节点能够优先扩展到达到深度界限为止。
从初始节点到目标节点一共需要26步,因此需要处理的时间比较长,在我自己电脑上的vs2019跑出来结果需要6200ms左右,当然这个时间还是可以接受的,而如果学会了启发式算法就不用这么多时间了。
核心代码如下(代码是以前在网上找的然后自己照着打然后改成自己容易理解的亚子了,侵删,侵删):
其中openTable存储带处理的节点,closeTable为已经处理的节点,使用 closeTable目的是方便存储路径。注:本来宽度优先搜索不需要range也就是深度优先的界限,因为是层层递进式的搜索,这里是非必要项,但是深度搜索中是需要的,避免单个节点一直搜索。
void bfs(deque<Node>& openTable, deque<Node>& closeTable, int range) {
while (!openTable.empty()) {
closeTable.push_back(openTable.front());
openTable.pop_front();
//节点n的深度是否等于深度界限
if (closeTable.back().deep > range) {
continue;
}
else {
/*
广度优先搜索和深度优先搜索的唯一区别就是子节点放到open表的位置:
(1)广度优先搜索放到open表的后面
(2)深度优先搜索放到open表的前面
*/
for (int j = 1; j <= 4; j++) {
Node tempNode = closeTable.back();
move(tempNode, j);//扩展节点n
if (tempNode.code == "") {
continue;
}
if (check_Visit(tempNode)) {
continue;
}
tempNode.parent = closeTable.back().code;
openTable.push_back(tempNode); //把其后裔节点放入OPEN表的前端
if (isGoal(tempNode)) {//是否有任何后继节点为目标节点
cout << "成功,宽度优先找到结果:" << endl;
showAnswer(closeTable);
return;//break;
}
}
}
}
}
以下是代码的全文:
#include"bfs.h"
void bfs(deque<Node>& openTable, deque<Node>& closeTable, int range) {
while (!openTable.empty()) {
closeTable.push_back(openTable.front());
openTable.pop_front();
//节点n的深度是否等于深度界限
if (closeTable.back().deep > range) {
continue;
}
else {
/*
广度优先搜索和深度优先搜索的唯一区别就是子节点放到open表的位置:
(1)广度优先搜索放到open表的后面
(2)深度优先搜索放到open表的前面
*/
for (int j = 1; j <= 4; j++) {
Node tempNode = closeTable.back();
move(tempNode, j);//扩展节点n
if (tempNode.code == "") {
continue;
}
if (check_Visit(tempNode)) {
continue;
}
tempNode.parent = closeTable.back().code;
openTable.push_back(tempNode); //把其后裔节点放入OPEN表的前端
if (isGoal(tempNode)) {//是否有任何后继节点为目标节点
cout << "成功,宽度优先找到结果:" << endl;
showAnswer(closeTable);
return;//break;
}
}
}
}
}
int main() {
clock_t start, finish;
start = clock();
init_Fact();
string originString = "724506831";
Node originNode;
deque<Node> openTable, closeTable;//open存放未扩展节点,closed存放已扩展节点
int range = 30;//深度界限
originNode.code = originString;
originNode.deep = 0;
check_Visit(originNode);
openTable.push_back(originNode);
bfs(openTable, closeTable, range);
cout << "open表使用大小:" <<openTable.size() << endl;
cout << "close表使用大小:" << closeTable.size() << endl;
finish = clock();
cout << (finish - start) << "毫秒" << endl;
//system("pause");
return 0;
}
#pragma once
#include<iostream>
#include<ctime>
#include<queue>
#include<cstring>
#include<list>
using namespace std;
const string GOAL = "012345678"; //254306781
int vis[362880 + 5], hashString[9];
struct Node {
string code;
string parent;
int deep;
};
Node move(Node& tempNode, int x) {
string::size_type loc;
for (int i = 0; i < 9; i++) {
if (tempNode.code[i] == '0') {
loc = i;//0的位置,从0开始计数
}
}
tempNode.deep++;
if (x == 1) {//上移
if (loc >= 3) {
char tempString = tempNode.code[loc];//找到空格
tempNode.code[loc] = tempNode.code[loc - 3];
tempNode.code[loc - 3] = tempString;
}
else {
tempNode.code = "";
}
}
else if (x == 2) {//下移
if (loc <= 5) {
char tempString = tempNode.code[loc];//找到空格
tempNode.code[loc] = tempNode.code[loc + 3];
tempNode.code[loc + 3] = tempString;
}
else {
tempNode.code = "";
}
}
else if (x == 3) {//左移
if (loc != 0 && loc != 3 && loc != 6) {
char tempString = tempNode.code[loc];//找到空格
tempNode.code[loc] = tempNode.code[loc - 1];
tempNode.code[loc - 1] = tempString;
}
else {
tempNode.code = "";
}
}
else if (x == 4) {//右移
if (loc != 2 && loc != 5 && loc != 8) {
char tempString = tempNode.code[loc];//找到空格
tempNode.code[loc] = tempNode.code[loc + 1];
tempNode.code[loc + 1] = tempString;
}
else {
tempNode.code = "";
}
}
return tempNode;
}
bool isGoal(Node tempNode) {//判断是否为最终的目标节点
if (tempNode.code == GOAL) {
return true;
}
return false;
}
void show(string tempString) {//输出当前字符串
if (!tempString.empty()) {
cout << tempString[0] << " " << tempString[1] << " " << tempString[2] << endl
<< tempString[3] << " " << tempString[4] << " " << tempString[5] << endl
<< tempString[6] << " " << tempString[7] << " " << tempString[8] << endl << endl;
}
else {
cout << "空的" << endl;
}
}
//路径
deque<Node> findPath(deque<Node>& closed) {
deque<Node> dequeList;
Node temp = closed.back();
dequeList.push_back(temp);
//closeTable表从后往前,根据parent值找到路径
for (int i = closed.size() - 1; i >= 0; i--) {
if (temp.parent == closed.at(i).code) {
dequeList.push_back(closed.at(i));
temp = closed.at(i);
}
}
//cout << dequeList.size() << endl;
return dequeList;
}
void showAnswer(deque<Node>& closed) {
deque<Node> way(findPath(closed));
cout << "共需要" << way.size() << "步" << endl;
for (int i = way.size() - 1; i >= 0; i--) {
show(way.at(i).code);
}
//输出目标
show(GOAL);
}
void init_Fact() {
hashString[0] = 1;
for (int i = 1; i < 9; ++i) hashString[i] = hashString[i - 1] * i;
memset(vis, 0, sizeof(0));
}
bool check_Visit(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;
}