A*算法(解决八数码难题)C语言实现

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>

#define LIST_INIT_SIZE 80 // 线性表存储空间的初始化分配量
#define ok 1
#define error -1

typedef int ElemType;
typedef int Status;

Status InitSqList_Sq();
void actionway();
int InitAxing();
void location();
void actionway();
int LocationFatherX();
int LocationFatherY();
void extendNode();
Status EmptyList_Sq();
Status ListDelete();
Status ListInsert();
void preextendNode();
int equality();
Status ListDeletefromOPEN();

// 终态的位置
int L[3][3] = {1, 2, 3,
               8, 0, 4,
               7, 6, 5};
int p = 3; // 行数
int q = 3; // 列数

typedef struct Node
{
  int action[4];  // 0的方向:上action[0] 下action[1]  左action[2]  右action[3]
                  //(值1:可以往当前方向前进;值0:不可以往当前方向前进)
  struct Node *p; // 指向父节点的指针
  int str[3][3];  // 保存当前状态信息的二维数组
  int deep;       // 深度
  int cost        // 位置错误的元素个数
} Node, *NodeList;

// 构造线性表
typedef struct SqList
{
  Node *elem;   // 存储空间基址(一维数组)
  int length;   // 当前的长度
  int listsize; // 当前分配的存储容量(以sizeof(ElemType)为单位)
  int tag;      // 1为CLOSE表 0为OPEN表
} SqList;

SqList OPEN;  // 构造OPEN表
SqList CLOSE; // 构造CLOSE表

int T = 1; // 记录

//线性表的初始化,构造一个空表
Status InitSqList_Sq(SqList *L)
{
  L->elem = (Node *)malloc(LIST_INIT_SIZE * sizeof(Node));
  if (!L->elem)
  {
    printf("存储空间分配失败\n");
    return error;
  }
  L->length = 0;
  L->listsize = LIST_INIT_SIZE;
  printf("线性表初始化完成\n");
  return ok;
}
// 线性表插入操作
Status ListInsert(SqList *L, Node *N)
{
  int i, j, k;
  int flag = 0; // 判断是否有通deep的节点插入到OPEN表中
  //存储空间已满
  if (L->length >= L->listsize)
    return error;
  // 插入到CLOSE表中
  if (L->tag == 1)
  {
    L->elem[L->length] = *N;
    L->length++;
  }
  // 插入到OPEN表中
  if (L->tag == 0)
  {
    // 是空表则直接加入
    if (EmptyList_Sq(L) == -1)
    {
      L->elem[L->length] = *N;
      L->length++;
    }
    else
    {
      for (i = 0; i < L->length; i++)
      {
        if (L->elem[i].deep == N->deep)
        {
          flag = 1;
          break;
        }
      }
      if (flag == 1)
      { // 有相同deep的节点插入到OPEN表中
        // 与前一个元素的cost+deep相等则加入
        if ((N->cost + N->deep) == (L->elem[L->length - 1].cost + L->elem[L->length - 1].deep))
        {
          L->elem[L->length] = *N;
          L->length++;
        }
        // cost+deep小于前一个
        if ((N->cost + N->deep) < (L->elem[L->length - 1].cost + L->elem[L->length - 1].deep))
        {
          // 将相同deep的元素全部从OPEN表中移除
          for (i = 0; i < L->length; i++)
          {
            if (L->elem[i].deep == N->deep)
            {
              ListDeletefromOPEN(&OPEN, i);
            }
          }
          L->elem[L->length] = *N;
          L->length++;
        }
        // cost + deep大于前一个什么都不执行
      }else if (flag == 0)
      {
        // 没有相同deep的节点插入到OPEN表中
        L->elem[L->length] = *N;
        L->length++;
      }
    }
  }
  return ok;
}

// 线性表删除操作(固定取第一个元素)
Status ListDelete(SqList *L, int i, Node *N)
{
  int j;

  Node *q = &(L->elem[i]);
  *N = *q;

  for (j = i; j < L->length; j++)
  {
    L->elem[j] = L->elem[j + 1];
  }

  L->length--;
  return ok;
}

// OPEN表的删除操作将某一次扩展的插入到OPEN表中的子节点全部删除
Status ListDeletefromOPEN(SqList *L, int i)
{
  int j;
  for (j = i; j < L->length; j++)
  {
    L->elem[j] = L->elem[L->length];
  }
  L->length = i;

  return ok;
}

//判断线性表是否为空
Status EmptyList_Sq(SqList *L)
{
  if (L->elem)
  {
    if (L->length != 0)
    {
      // printf("线性表不是空表\n");
      return ok;
    }
    else
    {
      // printf("线性表是空表\n");
      return error;
    }
  }
  else
    printf("线性表不存在\n");
  return error;
}

// 构造初始态的八数码
int InitAxing(Node *N)
{
  int i, j;

  printf("请输入3*3的二维数组(数字0~8):\n");
  for (i = 0; i < p; i++)
  {
    for (j = 0; j < q; j++)
    {
      scanf("%d", &N->str[i][j]);
    }
  }
  // printf("\n");
  // for(i = 0; i < p; i++){
  //   for(j = 0; j < q; j++){
  //     printf("%d ", &N->str[i][j]);
  //   }
  // }
  N->deep = 0;
  // 求位置错误的元素个数
  location(N);
  // 确定可以扩展的节点
  actionway(N);
}

// 求位置错误的元素个数
void location(Node *N)
{
  int i, j;
  int cost = 0;
  for (i = 0; i < p; i++)
  {
    for (j = 0; j < q; j++)
    {
      if (N->str[i][j] != L[i][j])
      {
        cost++;
      }
    }
  }
  N->cost = cost;
}

// 判断0上下左右那些位置可以走
void actionway(Node *N)
{
  int i, j;
  int n, m; // 记录0的位置
  for (i = 0; i < p; i++)
  {
    for (j = 0; j < q; j++)
    {
      if (N->str[i][j] == 0)
      {
        n = i;
        m = j;
        break;
      }
    }
  }
  // 能否向上
  if (N->deep == 0 && n > 0 || n > 0 && (n - 1) != LocationFatherX(N->p))
  {
    N->action[0] = 1;
  }
  else
  {
    N->action[0] = 0;
  }
  // 能否向左
  if (m > 0 && N->deep == 0 || m > 0 && (m - 1) != LocationFatherY(N->p))
  {
    N->action[2] = 1;
  }
  else
  {
    N->action[2] = 0;
  }
  // 能否向下
  if (n < 2 && N->deep == 0 || n < 2 && (n + 1) != LocationFatherX(N->p))
  {
    N->action[1] = 1;
  }
  else
  {
    N->action[1] = 0;
  }
  // 能否向右
  if (m < 2 && N->deep == 0 || m < 2 && (m + 1) != LocationFatherY(N->p))
  {
    N->action[3] = 1;
  }
  else
  {
    N->action[3] = 0;
  }
}

// 找父节点0的行位置
int LocationFatherX(Node *N)
{
  int i, j;
  for (i = 0; i < p; i++)
    for (j = 0; j < q; j++)
      if (N->str[i][j] == 0)
        return i;
}
// 找父节点0的列位置
int LocationFatherY(Node *N)
{
  int i, j;
  for (i = 0; i < p; i++)
    for (j = 0; j < q; j++)
      if (N->str[i][j] == 0)
        return j;
}

// 扩展结点的方法
void extendNode(Node *N, int k)
{
  int i, j;
  int temp;
  int n, m;       // 父节点0元素的下标
  Node childNode; // 创建子结点
  int str[3][3];
  childNode.p = N; // 子节点指向父节点
  childNode.deep = childNode.p->deep + 1;
  n = LocationFatherX(N);
  m = LocationFatherY(N);
  // 保存父节点的数组
  for (i = 0; i < p; i++)
  {
    for (j = 0; j < q; j++)
    {
      str[i][j] = N->str[i][j];
    }
  }
  // 0与上面的元素交换位置
  if (k == 0)
  {
    temp = str[n - 1][m];
    str[n - 1][m] = str[n][m];
    str[n][m] = temp;
  }
  // 0与下面的元素交换位置
  if (k == 1)
  {
    temp = str[n + 1][m];
    str[n + 1][m] = str[n][m];
    str[n][m] = temp;
  }
  // 0与左边的元素交换位置
  if (k == 2)
  {
    temp = str[n][m - 1];
    str[n][m - 1] = str[n][m];
    str[n][m] = temp;
  }
  // 0与右边的元素交换位置
  if (k == 3)
  {
    temp = str[n][m + 1];
    str[n][m + 1] = str[n][m];
    str[n][m] = temp;
  }
  // 创建子节点数组
  for (i = 0; i < p; i++)
  {
    for (j = 0; j < q; j++)
    {
      childNode.str[i][j] = str[i][j];
    }
  }
  // 判断子节点0可以往哪些方向移动
  actionway(&childNode);
  // 判断子节点位置错误的元素个数
  location(&childNode);

  for (i = 0; i < p; i++)
  {
    for (j = 0; j < q; j++)
    {
      printf("%d ", childNode.str[i][j]);
    }
  }
  printf("cost:%d+deep:%d", childNode.cost, childNode.deep);
  printf(" 可移动的方向:上%d,下%d,左%d,右%d\n", childNode.action[0], childNode.action[1], childNode.action[2], childNode.action[3]);

  ListInsert(&OPEN, &childNode);

}

// 扩展结点
void preextendNode(Node *N)
{
  int k = 0;
  while (k <= 3)
  {
    if (N->action[k] == 1)
    {
      extendNode(N, k);
    }
    k++;
  }
}

// 判断是否为目标结点
int equality(Node *N)
{
  int i, j;
  for (i = 0; i < p; i++)
  {
    for (j = 0; j < p; j++)
    {
      if (N->str[i][j] != L[i][j])
      {
        return error;
      }
    }
  }
  return ok;
}

int main()
{
  int i, j, k;
  InitSqList_Sq(&CLOSE);
  CLOSE.tag = 1;
  InitSqList_Sq(&OPEN);
  OPEN.tag = 0;
  printf("开始构造初始态:\n");
  Node initialstate;
  Node temp;
  InitAxing(&initialstate);
  // 将初始节点插入到OPEN表中
  ListInsert(&OPEN, &initialstate);
  // 扩展结点
  while (EmptyList_Sq(&OPEN) != -1)
  {
    // 将第一个节点从OPEN表中删除,并用temp返回其值
    ListDelete(&OPEN, 0, &temp);
    // 将结点加入到CLOSE表中
    ListInsert(&CLOSE, &temp);
    if (equality(&temp) == 1)
    {
      printf("找到目标结点\n");
      break;
    }
    else
    {
      preextendNode(&temp);
    }
    printf("\n");
    printf("步骤%d:\n", T++);
    printf("open表长度:%d\n", OPEN.length);
    for (i = 0; i < OPEN.length; i++)
    {
      for (j = 0; j < p; j++)
      {
        for (k = 0; k < q; k++)
        {
          printf("%d ", OPEN.elem[i].str[j][k]);
        }
      }
      printf("\n");
    }
  }
  printf("\n");
  printf("CLOSE表中走过的路径:\n");
  for (i = 0; i < CLOSE.length; i++)
  {
    for (j = 0; j < p; j++)
    {
      for (k = 0; k < q; k++)
      {
        printf("%d ", CLOSE.elem[i].str[j][k]);
      }
    }
    printf("\n");
  }

  return 0;
}

1:0的位置也算做了一次错误
2:没有判断无解的情况
在这里插入图片描述
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值