八数码问题的三种解决方式,其二:深度优先搜索 加自己的一些疑惑

接上文宽度优先搜索,虽然深度优先和宽度优先在理论上的差别不大,但是在最终的测试中我还是遇到了一些问题,导致26步计算不出来,只能用了一个中间状态来验证了一下思路正确而已。

这里先写一下自己的疑惑

假如0移动到target为目标状态,那么容易得到路径①为正确最短路径,但是在代码中初始0节点扩展时可能并不是向着下扩展,而是先向右扩展,那么会首先走向路径②,此时会深度优先会优先扩展路径②,这个时候假如沿着路径②走会产生两种错误,在本代码的运行结果中均有同样的错误产生。

  1. 在到达最大深度之前走到target,那么此时根据代码的判断已经找到路径,这个时候就会跳出整个while循环,进而输出②路径,但是②路径不是最优路径,当然这个错误是可以优化的,就是让while跑完,并不在找到一个解后就结束,然后将所有解都记录在一个STL中,最后对比路径长度,得到最短路径。但是又要涉及到第二个问题。
  2. 假设路径②走到第三步,也就是target节点的前一个节点就结束了,此时range刚好达到最大,这个时候路径②的深度优先就会被终止,进行其他路径的尝试,但是这个时候会产生一个巨大的问题,target的上一个节点已经被走过了,也就是说路径①走不了,因为访问当前节点是会先访问vis数组看当前节点是否被访问过,这个节点已经被访问了,所以路径②没有结果,路径①走不了。最后没有任何答案输出。

 

介于以上原因,我暂时没有想出第二个问题的解决方案,因此,我的深度优先算法存在一定的缺陷,所以我选择了到达目标过程中的一个中间过程,从初始状态到这个状态需要8步的状态来验证算法大致设计没有问题。结果如下,核心代码同BFS就不贴出来了。

直接贴全部代码:

#include"dfs.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_front(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 = 10;//深度界限
    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 = "254306781"; //012345678
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;
}

 

  • 4
    点赞
  • 15
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值