八数码宽度优先算法

相关代码要感谢网上的一些大神给予的分享

程序思路

结构体map

用来作为一个结点的数据结构。

struct Map
{
    int cell[N][N];//数码数组
    Direction BelockDirec;//操作符方向
    struct Map * Parent;//父节点
};

PrintMap

用来打印一个结点

void PrintMap(struct Map *map)
{
    cout<<"*************************************************"<<endl;
    for(int i=0;i<N;i++)
    {
        for(int j=0;j<N;j++)
        {
cout<<map->cell[i][j]<<"   ";
        }
cout<<endl;
    }
 cout<<"*************************************************"<<endl;
}

MoveMap

函数用来扩展一个结点的后继结点(上、下、左、右)

struct Map * MoveMap(struct Map * map,Direction Direct,bool CreateNewMap)
{
    struct Map * NewMap;


    //获取空闲格位置i,j
    int i,j;
    for(i = 0; i < N; i++)
    {
        bool HasGetBlankCell = false;
        for(j = 0; j < N; j++)
        {
            if(map->cell[i][j] == 0)
            {
                HasGetBlankCell = true;
                break;
            }
        }
        if(HasGetBlankCell)
            break;
    }
    //移动数字,t_i,t_j为移动后0的坐标
    int t_i = i,t_j = j;
    bool AbleMove = true;
    switch(Direct)
    {
    case Down:
        t_i++;
        if(t_i >= N)
            AbleMove=false;
        break;
    case Up:
        t_i--;
        if(t_i < 0)
            AbleMove=false;
        break;
    case Left:
        t_j--;
        if(t_j < 0)
            AbleMove=false;
        break;
    case Right:
        t_j++;
        if(t_j >= N)
            AbleMove=false;
        break;
    };

    if(!AbleMove)//不可以移动则返回原节点
    {
        return map;
    }

    if(CreateNewMap)
    {
        NewMap = new Map();
        for(int x = 0; x < N; x++)
            for(int y = 0; y < N; y++)
                NewMap->cell[x][y] = map->cell[x][y];
    }
    else
        NewMap = map;
/*移动即交换0与相应数字的位置*/
    NewMap->cell[i][j] = NewMap->cell[t_i][t_j];
    NewMap->cell[t_i][t_j] = 0;


    return NewMap;
}

RandomMap

函数用来随机产生beginMap

struct Map * RandomMap() //随机产生八数码数组
{
int a[9];
struct Map * NewMap;
    NewMap = new Map();
srand((unsigned)time(0));
for(int k = 0; k < 9; k++)
    {  
bool Isre = false;
a[k] = rand()%9;
for (int l = 0; l < k; l++)
{
if (a[k] == a[l])

Isre = true;
break;
}
}
if(Isre)
{
k = k - 1;
continue;
}
}
    for(int i = 0; i < N; i++)
    {    
for (int j = 0; j < N; j++)

NewMap->cell[i][j] = a[i*3+j];
}
    }
NewMap->Parent = NULL;
NewMap->BelockDirec = None;
return NewMap;
}

IsSuccess

函数用来判断某节点是否为目标结点

 bool IsSuccess(struct Map * map,struct Map * Target)
{
bool IsSuc = true;
    for(int i = 0; i < N; i++)
    {
        for(int j = 0; j < N; j++)
        {
            if(map->cell[i][j] != Target->cell[i][j])
{
IsSuc = false;
break;
}
        }
if(!IsSuc)
break;
    }
    return IsSuc;
}

BNF_Search

struct Map * BNF_Search(struct Map * begin,struct Map * Target)

用来进行宽度搜索(按照以下的流程)

因为CLOSED表是只进不出的,所以这里省略了CLOSED表,用P1保存CLOSED表中的最新结点。而OPEN表是一个队列,先进先出。程序是以链表作为数据结构的,而不是数组。所以可以通过指针回溯

//把begin结点放入OPEN表中

struct Map * p1, *p2, *p=NULL;//p1为要进入closed表中的结点,p2为p1扩展的结点,p为等于结果的结点
bool IsSucc = false;
queue<struct Map *> Queue;
if(IsSuccess(begin,Target))
return begin;
Queue.push(begin);


//begin是否为目标结点    

 if(IsSuccess(begin,Target))
return begin;

//OPEN表是否为空表,或是否已经找到结果              

  while(!Queue.empty() || p == NULL);

                     

//把第一个结点从OPEN表中移出,放入CLOSED表中(即p1中)

 p1 = Queue.front(); //返回queue中的第一个元素
Queue.pop();

//扩展当前结点

for (int i = 1; i <= 4; i++)
{
Direction Direct=(Direction)i;
            if(Direct == p1->BelockDirec)//跳过屏蔽方向,因为里面保存了之前前进的方向,防止回退
                continue;
p2 = MoveMap(p1,Direct,true);//p2为扩展的的结点

//如果可以扩展成功(即可以移动)则设置方向和父节点指针

 if(p2 != p1)//数码是否可以移动


p2->Parent = p1;
switch(Direct)//设置屏蔽方向,防止往回推
{
case Up:
p2->BelockDirec = Down;
break;
case Down:
p2->BelockDirec = Up;
break;
case Left:
p2->BelockDirec = Right;
break;
case Right:
p2->BelockDirec = Left;
break;
}
                      

//判断后继结点是否为目标结点,如果不是则将后继结点放入OPEN表中并循环从OPEN表中pop,如果是则返回当前结点

if (IsSuccess(p2,Target))
{
p = p2;
return p;
}
Queue.push(p2);
n++;

HasResult

函数返回一个结点逆序数的奇偶性用来判断八数码是否可以后的结果

如果奇偶相同说明有解,否则无解

int HasResult(struct Map *map) //返回逆序数的奇偶用来判断是否有解
{
int a=0;
char b[9];
//把map二维表变为数组
for(int i=0;i<N;i++)
{
for(int j=0;j<N;j++)
b[i*3+j]=map->cell[i][j];
}
for(i=0;i<9;i++)
{
for(int j=0;j<i;j++)
{
if((b[j]<b[i])&&b[j]!=0)
a++;
}
}
return a%2;
}

Main函数

随机生成begin结点,并定义target结点

定义a1,a2用来记录begin和target逆序数的奇偶性用以判断是否有解

int a1,a2;
Map Target;
Map *begin,*T;
begin=new Map;
    //设定目标图 [1 2 3],[8 0 4],[7 6 5]
Target.cell[0][0] = 1;
Target.cell[0][1] = 2;
Target.cell[0][2] = 3;
Target.cell[1][0] = 8;
Target.cell[1][1] = 0;
Target.cell[1][2] = 4;
Target.cell[2][0] = 7;
Target.cell[2][1] = 6;
Target.cell[2][2] = 5;
begin = RandomMap();
cout<<"目标图:"<<endl;
PrintMap(&Target);
cout<<"起始图:"<<endl;
    PrintMap(begin);
a1=HasResult(&Target);
a2=HasResult(begin);
if(a1!=a2)
cout<<"无解"<<endl;

如果有解则进行BNF搜索并打印结果:

else
{
double start=clock();
cout<<"有解"<<endl;

//图搜索
T=BNF_Search(begin,&Target);
//打印
if(T != NULL)
{
//把路径倒序
Map *p=T;
stack<Map *> result_Stack1;  //栈,先进后出
while(p->Parent != NULL)
{
result_Stack1.push(p);
p = p->Parent;
}
cout<<"搜索结果:"<<endl;
while(!result_Stack1.empty())
{
PrintMap(result_Stack1.top());
c++;
result_Stack1.pop();//删除
}
cout<<"\n完成!"<<endl;
cout<<"路径长度为:"<<c<<endl;
double end=clock();
cout<<"程序运行的时间为:"<<end-start<<endl;
}
}

程序测试

有解:


无解:


猜你喜欢

转载自blog.csdn.net/u014050788/article/details/50097977