为了有效地选择下一位置,可以将从位置[i][j]出发可能的前进方向预先定义在一个表内,按顺时针方向为N([i-1][j]),NE([i-1][j+1]),E([i][j+1]),SE([i+1][j+1]),S([i+1][j]),SW([i+1][j-1]),W([i][j-1]),NW([i-1][j-1])。
(1)前进方向示意图:
(2)前进方向表:
Move[q].dir | move[q].a | move[q].b |
---|---|---|
“N” | -1 | 0 |
“NE” | -1 | 1 |
“E” | 0 | 1 |
“SE” | 1 | 1 |
“S” | 1 | 0 |
“SW” | 1 | -1 |
“W” | 0 | -1 |
“NW” | -1 | -1 |
文件:MazeConfig.h
#pragma once
#include
#include
using namespace std;
//位置坐标和前进方向序号的三元组结构定义
struct items
{
int x;//位置的x坐标
int y;//位置的y坐标
int dir;//前进方向的序号
};
//前进方向表的结构定义
struct offfsets
{
int a;//x方向的偏移
int b;//y方向的偏移
char *dir;//移动的方向描述
};
const int m = 14;//迷宫的行数
const int n = 17;//迷宫的列数
const int dir_count = 8;//前进方向的总数
const int pathmark = 6;//迷宫通路的标识值
static int mark[m][n];//访问标记数组
items entry = { 1, 0, 0 };//迷宫入口网格坐标
items exitus = { m - 2, n - 1, 0 };//迷宫出口网格坐标
//各个方向的偏移表定义
offfsets moves[dir_count] =
{
{ -1, 0, "N" },
{ -1, 1, "NE" },
{ 0, 1, "E" },
{ 1, 1, "SE" },
{ 1, 0, "S" },
{ 1, -1, "SW" },
{ 0, -1, "W" },
{ -1, -1, "NW" }
};
//初始化迷宫
int Maze[m][n] =
{
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1 },
{ 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1 },
{ 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1 },
{ 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1 },
{ 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1 },
{ 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1 },
{ 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1 },
{ 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
};
//初始化访问标记数组
void init_mark()
{
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
mark[i][j] = 0;
}
}
}
//打印迷宫
void print_maze()
{
cout << "======>MazePath" << endl;
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
if (Maze[i][j] == pathmark)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);
}
else
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);
}
cout << Maze[i][j] << " ";
}
cout << endl;
}
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);
}
文件:SeekPath.h
#pragma once
#include "MazeConfig.h"
//从迷宫入口[entry.x][entry.y]开始,寻找通向出口的一条路径。
//如果找到,则函数返回true。
//如果没找到,则函数返回false。
bool SeekPath(items tmp)
{
if ((tmp.x == exitus.x) && (tmp.y == exitus.y))
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);
cout << "======>SeekPath Success" << endl;
return true;//已到达出口,函数返回1。
}
items next;//下一个网格的位置
for (int d = 0; d < dir_count; d++)//依次按每一个方向寻找通向出口的路径
{
next.x = tmp.x + moves[d].a;
next.y = tmp.y + moves[d].b;
if ((Maze[next.x][next.y] == 0) && (mark[next.x][next.y] == 0))
{
//下一位置可通,试探该方向
mark[next.x][next.y] = 1;//标记为已访问过
if (SeekPath(next) == true)//从此位置递归试探
{
//cout << "(" << nextGrid.x << "," << nextGrid.y << ")," << "Direction:" << moveTable[i].dir << ", ";
Maze[next.x][next.y] = pathmark;
return true;//试探成功,逆向输出路径坐标
}
}
//回溯,换一个方向再试探通向出口的路径。
}
if ((tmp.x == entry.x) && (tmp.y == entry.y))
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);
cout << "======>SeekPath Fail" << endl;
}
return false;//无可通路到出口,函数返回0。
}
文件:main.cpp
#include "SeekPath.h"
int main()
{
init_mark();
print_maze();
if (SeekPath(entry) == true)
{
//cout << "(" << entry.x << "," << entry.y << ")" << endl;
Maze[entry.x][entry.y] = pathmark;
}
print_maze();
system("pause");
return 0;
}
文件:LinkedStack.h
#ifndef LINKED_STACK_H_
#define LINKED_STACK_H_
#include
using namespace std;
template <class T>
struct LinkNode //链表结点类的定义
{
T data; //数据域
LinkNode *link; //指针域——后继指针
//仅初始化指针成员的构造函数
LinkNode(LinkNode* ptr = NULL){ link = ptr; }
//初始化数据与指针成员的构造函数
LinkNode(const T& value, LinkNode* ptr = NULL){ data = value; link = ptr; }
};
template <class T>
class LinkedStack
{
public:
LinkedStack(); //构造函数
~LinkedStack(); //析构函数
public:
void Push(const T& x) ; //新元素x进栈
bool Pop(T& x); //栈顶元素出栈,并将该元素的值保存至x
LinkNode* getTop() const; //获取栈顶结点
bool IsEmpty() const; //判断栈是否为空
void MakeEmpty(); //清空栈的内容
private:
LinkNode *top; //栈顶指针,即链头指针
};
//构造函数
template <class T>
LinkedStack::LinkedStack()
: top(NULL)
{
cout << "$ 执行构造函数" << endl;
}
//析构函数
template <class T>
LinkedStack::~LinkedStack()
{
cout << "$ 执行析构函数" << endl;
MakeEmpty();
}
//新元素x进栈
template <class T>
void LinkedStack::Push(const T& x)
{
LinkNode *newNode = new LinkNode(x);
newNode->link = top;
top = newNode;
}
//栈顶元素出栈,并将该元素的值保存至x
template <class T>
bool LinkedStack::Pop(T& x)
{
if (true == IsEmpty())
{
return false;
}
LinkNode *curNode = top;
top = top->link;
x = curNode->data;
delete curNode;
return true;
}
//获取栈顶结点
template <class T>
LinkNode* LinkedStack::getTop() const
{
return top;
}
//判断栈是否为空
template <class T>
bool LinkedStack::IsEmpty() const
{
return (NULL == top) ? true : false;
}
//清空栈的内容
template <class T>
void LinkedStack::MakeEmpty()
{
LinkNode *curNode = NULL;
while (NULL != top) //当链表不为空时,删去链表中所有结点
{
curNode = top; //保存被删结点
top = curNode->link; //被删结点的下一个结点成为头结点
delete curNode; //从链表上摘下被删结点
}
}
#endif /* LINKED_STACK_H_ */
文件:MazeConfig.h
#ifndef MAZECONFIG_H_
#define MAZECONFIG_H_
#include
#include
using namespace std;
//位置坐标和前进方向序号的三元组结构定义
struct items
{
int x;//位置的x坐标
int y;//位置的y坐标
int dir;//前进方向的序号
};
//前进方向表的结构定义
struct offfsets
{
int a;//x方向的偏移
int b;//y方向的偏移
char *dir;//移动的方向描述
};
const int m = 14;//迷宫的行数
const int n = 17;//迷宫的列数
const int dir_count = 8;//前进方向的总数
const int pathmark = 6;//迷宫通路的标识值
static int mark[m][n];//访问标记数组
items entry = { 1, 0, 0 };//迷宫入口网格坐标
items exitus = { m - 2, n - 1, 0 };//迷宫出口网格坐标
//各个方向的偏移表定义
offfsets moves[dir_count] =
{
{ -1, 0, "N" },
{ -1, 1, "NE" },
{ 0, 1, "E" },
{ 1, 1, "SE" },
{ 1, 0, "S" },
{ 1, -1, "SW" },
{ 0, -1, "W" },
{ -1, -1, "NW" }
};
//初始化迷宫
int Maze[m][n] =
{
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1 },
{ 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1 },
{ 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1 },
{ 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1 },
{ 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1 },
{ 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1 },
{ 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
{ 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1 },
{ 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1 },
{ 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1 },
{ 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0 },
{ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
};
//初始化访问标记数组
void init_mark()
{
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
mark[i][j] = 0;
}
}
}
//打印迷宫
void print_maze()
{
cout << "======>MazePath" << endl;
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
if (Maze[i][j] == pathmark)
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);
}
else
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);
}
cout << Maze[i][j] << " ";
}
cout << endl;
}
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY);
}
#endif /* MAZECONFIG_H_ */
文件:SeekPath.h
#ifndef SEEKPATH_H_
#define SEEKPATH_H_
#include "MazeConfig.h"
#include "LinkedStack.h"
template
void MarkPath(LinkedStack* linkedStack)
{
LinkNode *curNode = linkedStack->getTop();
while (NULL != curNode)
{
items item = curNode->data;
Maze[item.x][item.y] = pathmark;
curNode = curNode->link;
}
}
//从迷宫入口[entry.x][entry.y]开始,寻找通向出口[m][p]的一条路径。
bool SeekPath()
{
init_mark();
LinkedStack *linkedStack = new LinkedStack; //设置工作栈
items tmp = entry; //初始化坐标方向为入口
items cur, next;
linkedStack->Push(tmp); //初始化的坐标方向三元组进栈
while (linkedStack->IsEmpty() == false) //栈不为空时,继续进行搜索
{
linkedStack->Pop(tmp); //退栈
cur.x = tmp.x; cur.y = tmp.y; cur.dir = tmp.dir; //当前位置坐标和下一个前进方向的序号
while (cur.dir < dir_count) //还有移动,继续移动
{
next.x = cur.x + moves[cur.dir].a; next.y = cur.y + moves[cur.dir].b; next.dir = 0; //找下一个位置的坐标
if ((next.x == exitus.x) && (next.y == exitus.y)) //到达出口
{
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_GREEN);
cout << "======>SeekPath Success" << endl;
linkedStack->Push(cur);
linkedStack->Push(exitus);
MarkPath(linkedStack);
delete linkedStack;
return true;
}
if ((Maze[next.x][next.y] == 0) && (mark[next.x][next.y] == 0))
{
mark[next.x][next.y] = 1;//标记为已访问过
tmp.x = cur.x; tmp.y = cur.y; tmp.dir = cur.dir; //记忆已通过位置和前进方向
linkedStack->Push(tmp); //进栈
cur.x = next.x; cur.y = next.y; cur.dir = next.dir; //移动到下一个网格,在各个方向试探
}
else
{
cur.dir++; //试探下一个方向
}
}
}
SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_INTENSITY | FOREGROUND_RED);
cout << "======>SeekPath Fail" << endl;
delete linkedStack;
return false;
}
#endif /* SEEKPATH_H_ */
文件:main.cpp
#include "SeekPath.h"
int main(int argc, char* argv[])
{
print_maze();
SeekPath();
print_maze();
system("pause");
return 0;
}
参考文献:
[1]《数据结构(用面向对象方法与C++语言描述)(第2版)》殷人昆——第三章
[2] 百度搜索关键字:迷宫问题、回溯法、试探法