首页 > 编程语言 >A*算法求解八数码问题

A*算法求解八数码问题

时间:2024-05-30 21:59:08浏览次数:12  
标签:结点 求解 int Head theNode next PNode 算法 数码

一、

问题描述:

A*算法是一种启发式图搜索算法,其特点在于对估价函数的定义上。对于一般的启发式图搜索,总是选择估价函数f值最小的节点作为扩展节点。因此,f是根据需要找到一条最小代价路径的观点来估算节点的,所以,考虑每个节点n的估价函数值为两个分量:f(n)=g(n)+h(n),从起始节点到节点n的实际代价g(n),从节点n到达目标节点的估价代价h(n)(不在点位个数),

八数码问题的求解

八数码问题是在3×3的九宫格棋盘上,摆有8个刻有1~8数码的将牌。棋盘中有一个空格,允许紧邻空格的某一将牌可以移到空格中,这样通过平移将牌可以将某一将牌布局变换为另一布局。给定的一种初始布局,目标状态,问如何移动将牌,实现从初始状态到目标状态的转变。

二、

算法设计:

将起始点加入open表

     当open表不为空时:

       寻找open表中f值最小的点current

       它是终止点,则找到结果,程序结束。

       否则,在Open表中移出current,对current表中的每一个临近点

           若它不可走或在close表中,略过

           若它不在open表中,加入。

           若它在open表中,计算g值,若g值更小,替换其父节点为current,更新

它的g值。

     若open表为空,则路径不存在。

算法流程图:

数据结构:用于记录最佳路径的树,两个链表,open表和close表

三、实现与实例

初始状态:2,8,3,0,6,4,1,7,5

目标状态:1,2,3,8,0,4,7,6,5

运行结果:

第0层的状态:

2 8 3

0 6 4

1 7 5

第1层的状态:

2 8 3

1 6 4

0 7 5

第2层的状态:

2 8 3

1 6 4

7 0 5

第3层的状态:

2 8 3

1 0 4

7 6 5

第4层的状态:

2 0 3

1 8 4

7 6 5

第5层的状态:

0 2 3

1 8 4

7 6 5

第6层的状态:

1 2 3

0 8 4

7 6 5

第7层的状态:

1 2 3

8 0 4

7 6 5

生成节点数目:20

扩展节点数目:10

运行时间:16.000000

代码实现:

#include "iostream"  

#include "stdlib.h"  

#include "conio.h"  

#include <math.h>

#include <windows.h>

using namespace std;

//定义二维数组来存储数据表示某一个特定状态  

typedef int status[3][3];

//定义 状态图中的结点数据结构  

typedef struct Node

{

    status data;//结点所存储的状态 ,一个3*3矩阵

    struct Node* parent;//指向结点的父亲结点  

    struct SpringLink* child;//指向结点的后继结点  

    struct Node* next;//指向open或者closed表中的后一个结点  

    int fvalue;//结点的估价函数值

    int gvalue;//结点的深度

    int hvalue;//结点到目标节点的预估代价值

}NNode, * PNode;

//定义存储指向结点后继结点的指针的地址  

typedef struct SpringLink

{

    struct Node* pointData;//指向结点的指针  

    struct SpringLink* next;//指向兄第结点  

}SPLink, * PSPLink;

PNode open;

PNode closed;

//OPEN表保存所有已生成而未考察的节点,CLOSED表中记录已访问过的节点

//开始状态与目标状态  

int t = 0; //迭代次数,相当于运行时间

int count_extendnode = 0;//扩展结点

int count_sumnode = 0; //生成节点

status startt = { 2,8,3,0,6,4,1,7,5 }; //实验报告

status target = { 1,2,3,8,0,4,7,6,5 };

//初始化空链表  

void initLink(PNode& Head)

{

    Head = (PNode)malloc(sizeof(NNode));

    Head->next = NULL;

}

//判断链表是否为空  

bool isEmpty(PNode Head)

{

    if (Head->next == NULL)

        return true;

    else

        return false;

}

//链表中弹出一个数据  

void popNode(PNode& Head, PNode& FNode)

{

    if (isEmpty(Head))

    {

        FNode = NULL;

        return;

    }

    FNode = Head->next;

    Head->next = Head->next->next;

    FNode->next = NULL;

}

//向结点的最终后继结点链表中添加新的子结点  

void addSpringNode(PNode& Head, PNode newData)

{

    PSPLink newNode = (PSPLink)malloc(sizeof(SPLink));

    newNode->pointData = newData;

    newNode->next = Head->child;

    Head->child = newNode;

}

//释放状态图中存放结点后继结点地址的空间  

void freeSpringLink(PSPLink& Head)

{

    PSPLink tmm;

    while (Head != NULL)

    {

        tmm = Head;

        Head = Head->next;

        free(tmm);

    }

}

//释放open表与closed表中的资源  

void freeLink(PNode& Head)

{

    PNode tmn;

    tmn = Head;

    Head = Head->next;

    free(tmn);

    while (Head != NULL)

    {

        //首先释放存放结点后继结点地址的空间  

        freeSpringLink(Head->child);

        tmn = Head;

        Head = Head->next;

        free(tmn);

    }

}

//向普通链表中添加一个结点  

void addNode(PNode& Head, PNode& newNode)

{

    newNode->next = Head->next;

    Head->next = newNode;

}

//向非递减排列的链表中添加一个结点(已经按照权值进行排序)  

void addAscNode(PNode& Head, PNode& newNode)

{

    PNode P;

    PNode Q;

    P = Head->next;

    Q = Head;

    while (P != NULL && P->fvalue < newNode->fvalue)

    {

        Q = P;

        P = P->next;

    }

    //上面判断好位置之后,下面就是简单的插入了  

    newNode->next = Q->next;

    Q->next = newNode;

}

//计算结点的 h 值  f=g+h. 按照不在位的个数进行计算

int computeHValue(PNode theNode)

{

    int num = 0;

    for(int i = 0 ; i < 3 ; i++)

    {

        for(int j = 0 ; j < 3 ; j++)

        {

            if(theNode->data[i][j] != target[i][j])

                num++;

        }

    }

    return num;

}

//计算结点的f,g,h值  

void computeAllValue(PNode& theNode, PNode parentNode)

{

    if (parentNode == NULL)

        theNode->gvalue = 0;

    else

        theNode->gvalue = parentNode->gvalue + 1;

       theNode->hvalue = computeHValue(theNode);  

   

    theNode->fvalue = theNode->gvalue + theNode->hvalue;

}

//初始化函数,进行算法初始条件的设置  

void initial()

{

    //初始化open以及closed表  

    initLink(open);

    initLink(closed);

    //初始化起始结点,令初始结点的父节点为空结点  

    PNode NULLNode = NULL;

    PNode Start = (PNode)malloc(sizeof(NNode));

    for (int i = 0; i < 3; i++)

    {

        for (int j = 0; j < 3; j++)

        {

            Start->data[i][j] = startt[i][j];

        }

    }

    Start->parent = NULL;

    Start->child = NULL;

    Start->next = NULL;

    computeAllValue(Start, NULLNode);

    //起始结点进入open表   

    addAscNode(open, Start);

}

//将B节点的状态赋值给A结点  

void statusAEB(PNode& ANode, PNode BNode)

{

    for (int i = 0; i < 3; i++)

    {

        for (int j = 0; j < 3; j++)

        {

            ANode->data[i][j] = BNode->data[i][j];

        }

    }

}

//两个结点是否有相同的状态  

bool hasSameStatus(PNode ANode, PNode BNode)

{

    for (int i = 0; i < 3; i++)

    {

        for (int j = 0; j < 3; j++)

        {

            if (ANode->data[i][j] != BNode->data[i][j])

                return false;

        }

    }

    return true;

}

//结点与其祖先结点是否有相同的状态  

bool hasAnceSameStatus(PNode OrigiNode, PNode AnceNode)

{

    while (AnceNode != NULL)

    {

        if (hasSameStatus(OrigiNode, AnceNode))

            return true;

        AnceNode = AnceNode->parent;

    }

    return false;

}

//取得方格中空的格子的位置  

void getPosition(PNode theNode, int& row, int& col)

{

    for (int i = 0; i < 3; i++)

    {

        for (int j = 0; j < 3; j++)

        {

            if (theNode->data[i][j] == 0)

            {

                row = i;

                col = j;

                return;

            }

        }

    }

}

//交换两个数字的值  

void changeAB(int& A, int& B)

{

    int C;

    C = B;

    B = A;

    A = C;

}

//检查相应的状态是否在某一个链表中  

bool inLink(PNode spciNode, PNode theLink, PNode& theNodeLink, PNode& preNode)

{

    preNode = theLink;

    theLink = theLink->next;

    while (theLink != NULL)

    {

        if (hasSameStatus(spciNode, theLink))

        {

            theNodeLink = theLink;

            return true;

        }

        preNode = theLink;

        theLink = theLink->next;

    }

    return false;

}

//产生结点的后继结点(与祖先状态不同)链表  

void SpringLink(PNode theNode, PNode& spring)

{

    int row;

    int col;

    getPosition(theNode, row, col);

    //空的格子右边的格子向左移动  

    if (col != 2)

    {

        PNode rlNewNode = (PNode)malloc(sizeof(NNode));

        statusAEB(rlNewNode, theNode);

        changeAB(rlNewNode->data[row][col], rlNewNode->data[row][col + 1]);

        if (hasAnceSameStatus(rlNewNode, theNode->parent))

        {

            free(rlNewNode);//与父辈相同,丢弃本结点  

        }

        else

        {

            rlNewNode->parent = theNode;

            rlNewNode->child = NULL;

            rlNewNode->next = NULL;

            computeAllValue(rlNewNode, theNode);

            //将本结点加入后继结点链表  

            addNode(spring, rlNewNode);

        }

    }

    //空的格子左边的格子向右移动  

    if (col != 0)

    {

        PNode lrNewNode = (PNode)malloc(sizeof(NNode));

        statusAEB(lrNewNode, theNode);

        changeAB(lrNewNode->data[row][col], lrNewNode->data[row][col - 1]);

        if (hasAnceSameStatus(lrNewNode, theNode->parent))

        {

            free(lrNewNode);//与父辈相同,丢弃本结点  

        }

        else

        {

            lrNewNode->parent = theNode;

            lrNewNode->child = NULL;

            lrNewNode->next = NULL;

            computeAllValue(lrNewNode, theNode);

            //将本结点加入后继结点链表  

            addNode(spring, lrNewNode);

        }

    }

    //空的格子上边的格子向下移动  

    if (row != 0)

    {

        PNode udNewNode = (PNode)malloc(sizeof(NNode));

        statusAEB(udNewNode, theNode);

        changeAB(udNewNode->data[row][col], udNewNode->data[row - 1][col]);

        if (hasAnceSameStatus(udNewNode, theNode->parent))

        {

            free(udNewNode);//与父辈相同,丢弃本结点  

        }

        else

        {

            udNewNode->parent = theNode;

            udNewNode->child = NULL;

            udNewNode->next = NULL;

            computeAllValue(udNewNode, theNode);

            //将本结点加入后继结点链表  

            addNode(spring, udNewNode);

        }

    }

    //空的格子下边的格子向上移动  

    if (row != 2)

    {

        PNode duNewNode = (PNode)malloc(sizeof(NNode));

        statusAEB(duNewNode, theNode);

        changeAB(duNewNode->data[row][col], duNewNode->data[row + 1][col]);

        if (hasAnceSameStatus(duNewNode, theNode->parent))

        {

            free(duNewNode);//与父辈相同,丢弃本结点  

        }

        else

        {

            duNewNode->parent = theNode;

            duNewNode->child = NULL;

            duNewNode->next = NULL;

            computeAllValue(duNewNode, theNode);

            //将本结点加入后继结点链表  

            addNode(spring, duNewNode);

        }

    }

}

//输出给定结点的状态  

void outputStatus(PNode stat)

{

    for (int i = 0; i < 3; i++)

    {

        for (int j = 0; j < 3; j++)

        {

            cout << stat->data[i][j] << " ";

        }

        cout << endl;

    }

}

//输出最佳的路径  

void outputBestRoad(PNode goal)

{

    int deepnum = goal->gvalue;

    if (goal->parent != NULL)

    {

        outputBestRoad(goal->parent);

    }

    cout << "第" << deepnum-- << "层的状态:" << endl;

    outputStatus(goal);

}

void AStar()

{

    PNode tmpNode;//指向从open表中拿出并放到closed表中的结点的指针  

    PNode spring;//tmpNode的后继结点链  

    PNode tmpLNode;//tmpNode的某一个后继结点  

    PNode tmpChartNode;

    PNode thePreNode;//指向将要从closed表中移到open表中的结点的前一个结点的指针  

    bool getGoal = false;//标识是否达到目标状态  

    long numcount = 1;//记录从open表中拿出结点的序号  

    initial();//对函数进行初始化  

    initLink(spring);//对后继链表的初始化  

    tmpChartNode = NULL;

    cout << "1" << endl;

    PNode Target = (PNode)malloc(sizeof(NNode));

    for (int i = 0; i < 3; i++)

    {

        for (int j = 0; j < 3; j++)

        {

            Target->data[i][j] = target[i][j];

        }

    }

    cout << "1" << endl;

    cout << "从open表中拿出的结点的状态及相应的值" << endl;

    while (!isEmpty(open))

    {

        t++;

        //从open表中拿出f值最小的元素,并将拿出的元素放入closed表中  

        popNode(open, tmpNode);

        addNode(closed, tmpNode);

        count_extendnode = count_extendnode + 1;

        cout << "第" << numcount++ << "个状态是:" << endl;

        outputStatus(tmpNode);

        cout << "其f值为:" << tmpNode->fvalue << endl;

        cout << "其g值为:" << tmpNode->gvalue << endl;

        cout << "其h值为:" << tmpNode->hvalue << endl;

        //如果拿出的元素是目标状态则跳出循环

         if(computeHValue(tmpNode) == 0)

         {  count_extendnode=count_extendnode-1;

             getGoal = true;

             break;

         }

         //如果拿出的元素是目标状态则跳出循环  

        if (hasSameStatus(tmpNode, Target) == true)

        {

            count_extendnode = count_extendnode - 1;

            getGoal = true;

            break;

        }

        //产生当前检测结点的后继(与祖先不同)结点列表,产生的后继结点的parent属性指向当前检测的结点  

        SpringLink(tmpNode, spring);

        //遍历检测结点的后继结点链表  

        while (!isEmpty(spring))

        {

            popNode(spring, tmpLNode);

            //状态在open表中已经存在,thePreNode参数在这里并不起作用  

            if (inLink(tmpLNode, open, tmpChartNode, thePreNode))

            {

                addSpringNode(tmpNode, tmpChartNode);

                if (tmpLNode->gvalue < tmpChartNode->gvalue)

                {

                    tmpChartNode->parent = tmpLNode->parent;

                    tmpChartNode->gvalue = tmpLNode->gvalue;

                    tmpChartNode->fvalue = tmpLNode->fvalue;

                }

                free(tmpLNode);

            }

            //状态在closed表中已经存在  

            else if (inLink(tmpLNode, closed, tmpChartNode, thePreNode))

            {

                addSpringNode(tmpNode, tmpChartNode);

                if (tmpLNode->gvalue < tmpChartNode->gvalue)

                {

                    PNode commu;

                    tmpChartNode->parent = tmpLNode->parent;

                    tmpChartNode->gvalue = tmpLNode->gvalue;

                    tmpChartNode->fvalue = tmpLNode->fvalue;

                    freeSpringLink(tmpChartNode->child);

                    tmpChartNode->child = NULL;

                    popNode(thePreNode, commu);

                    addAscNode(open, commu);

                }

                free(tmpLNode);

            }

            //新的状态即此状态既不在open表中也不在closed表中  

            else

            {

                addSpringNode(tmpNode, tmpLNode);

                addAscNode(open, tmpLNode);

                count_sumnode += 1;//生成节点  

            }

        }

    }

    //目标可达的话,输出最佳的路径  

    if (getGoal)

    {

        cout << endl;

        cout << "最佳路径长度为:" << tmpNode->gvalue << endl;

        cout << "最佳路径为:" << endl;

        outputBestRoad(tmpNode);

    }

    //释放结点所占的内存  

    freeLink(open);

    freeLink(closed);

    //    getch();  

}

int main()

{

    double start = GetTickCount();

    AStar();

    printf("生成节点数目:%d\n", count_sumnode);

    printf("扩展节点数目:%d\n", count_extendnode);

    printf("运行时间:%f\n", GetTickCount() - start);

    return 0;

}

标签:结点,求解,int,Head,theNode,next,PNode,算法,数码
From: https://blog.csdn.net/Vernel/article/details/139334691

相关文章

  • 基于DCT变换和位平面分解的数字水印嵌入提取算法matlab仿真
    1.算法运行效果图预览    2.算法运行软件版本matlab2022a 3.算法理论概述    基于离散余弦变换(DiscreteCosineTransform,DCT)和位平面分解(Bit-PlaneDecomposition)的数字水印嵌入与提取算法,是一种结合了频域与空域特性的稳健数字水印技术。该方法利......
  • 计算机算法中的数字表示法——定点数
    目录1.前言2.什么是定点数3.定点数如何去表示数字4.定点数表示法的局限性1.前言前面一篇文章讲了计算机中的数字表示法:原码、补码和反码,这一篇文章开始进行定点数的讲解。2.什么是定点数定点数,从字面意思上理解就是小数点位置固定,如下图所示:数字既包括整数,又包括......
  • 比亚迪算法岗面试,问的贼细!
    节前,我们组织了一场算法岗技术&面试讨论会,邀请了一些互联网大厂朋友、今年参加社招和校招面试的同学。针对大模型技术趋势、大模型落地项目经验分享、新手如何入门算法岗、该如何准备面试攻略、面试常考点等热门话题进行了深入的讨论。总结链接如下:《大模型面试宝典》(20......
  • 程序分享--常见算法/编程面试题:不使用额外数组空间,原地移除数组中给定元素
    关注我,持续分享逻辑思维&管理思维&面试题;可提供大厂面试辅导、及定制化求职/在职/管理/架构辅导;有意找工作的同学,请参考博主的原创:《面试官心得--面试前应该如何准备》,《面试官心得--面试时如何进行自我介绍》, 《做好面试准备,迎接2024金三银四》。或关注博主免费专栏【程序......
  • OSPF状态机+SPF算法
      OSPF状态机1.点到点网络类型   down-->init-->(前提为可以建立邻接)exstart——>exchange-->若查看邻接的DBD目录后发现不用进行LSA直接进入ful。若查看后需要进行查询、应答先进入loading,在查询应答完后再进入fuIl:2.MA网络类型   down-->init-->2way-......
  • 优化Python中的数据结构与算法(指南)
    ......
  • 代码随想录算法训练营第四十四天|01 背包、动态规划:01背包理论基础(滚动数组)、416. 分
    01背包文档讲解:代码随想录题目链接:46.携带研究材料(第六期模拟笔试)有n件物品和一个最多能背重量为w的背包。第i件物品的重量是weight[i],得到的价值是value[i]。每件物品只能用一次,求解将哪些物品装入背包里物品价值总和最大。 暴力解法:每个物品都有放与不放两种状态......
  • 代码随想录算法训练营Day55 | 583. 两个字符串的删除操作、72. 编辑距离、编辑距离总
    本文目录583.两个字符串的删除操作做题看文章72.编辑距离做题看文章编辑距离总结篇以往忽略的知识点小结个人体会583.两个字符串的删除操作代码随想录:583.两个字符串的删除操作Leetcode:583.两个字符串的删除操作做题找出最长公共子序列,然后用两个字符串的......
  • 大模型算法办备案全网最详细说明(+附件)
    ​已成功备案产品(近130家,不包括审核中的)一、大模型算法备案的强制性二、生成式人工智能(大语言模型)安全评估要点三、大模型备案必备材料+重点说明四、大模型备案填报流程五、大模型备案时间成本对比六、备案建议附录、过程性材料关于备案咨询不论最终是找我们做备案,......
  • 基于k-means算法的用户进行聚类项目(免费提供全部源码)
    下载地址如下:基于k-means算法的用户进行聚类项目(免费提供全部源码)资源-CSDN文库项目介绍背景在大数据时代,用户数据的收集和分析变得尤为重要。企业通过分析用户行为数据,可以更好地理解客户需求,提升服务质量,从而在市场竞争中占据有利位置。然而,随着数据量的增大和数据种类的......