摘 要
在现实生活中,有许多路径的设计问题,比如铺设路面、铺设线路、铺设水管等等,面对这些问题,我们需要找到许多不同的路径,需要考虑到具体生活中的某些问题,比如地形地貌、适不适合从某个地点通过,只有合理的设计线路,才能更好的构建幸福家园,所以构建路径就显得尤为重要。
第一章 绪 论
1.1 课设主要研究问题
迷宫问题是取自心理学的一个古典实验。在该实验中,把一只老鼠从一个无顶大盒子的门放入,在盒子中设置了许多墙,对行进方向形成了多处阻挡。盒子仅有一个出口,在出口处放置块奶酪,吸引老鼠在迷宫中寻找道路以到达出口。对同一只老鼠重复进行上述实验,一直到老鼠从入口走到出口,而不走错-一步。老鼠经过多次试验最终学会走通迷宫的路线。设计一个计算机程序对任意设定的矩形迷宫如下图A所示,求出一条从入口到出口的通路,或得出没有通路的结论。
1.2 课设应用的理论知识
1.2.1 邻接表
邻接表(Adjacency List)是图的一种链式存储结构。在邻接表中,对图中每个顶点Vi建立一个单链表,把与Vi相邻接的顶点放在这个链表中。邻接表中每个单链表的第一个节点存放有关顶点的信息,把这一节点看成链表的表头,其余节点存放有关边的信息,这样邻接表便由两部分组成:表头节点表和边表。
(1)表头节点表:由所有表头节点以顺序结构的形式存储,以便可以随机访问任一顶点的边链表。表头节点包括数据域(data)和链域(frstarc)两部分,如图(a)所示。其中,数据域用于存储顶点Vi的名称或其他有关信息:链域用于指向链表中第一个节点(与顶点口邻接的第一个邻接点)。
边表:由表示图中顶点间关系的2n个边链表组成。边链表中边节点包括邻接点域(adivex)、数据域(info)和链域(nextare)了个部分,如图(b)所示。其中,邻接点域指示与顶点,邻接的点在图中的位置;数据域存储和边相关的信息,如权值等;链域指示与顶点Vi邻接的下一条边的节点。
图的遍历:和树的遍历类似,图的遍历也是从图中某一项点出发,拔照某种方法对图中所有项点进行访问且仅访问一次。图的遍历算法是求解因的连通性问题、拓扑排序和关键路径等算法的基础。然而,图的過历要比树的適历复杂得多。因为图的任一项点都可能和其余的顾点相邻拉,所以在访问了某个顶点之后,可能沿着某条路径搜索之后,又回到该顶点上。为了避免同一项点被访问至次,在遍历图的过程中,必领记下每个已访问过的顶点。为此,
设一个铺助数组visited[n],其初始值置为“false”或者0,一旦访问了顶点Vi,便置visited[i]为“true”或者1。
深度优先搜索:
深度优先搜索遍历的过程
,深度优先搜素(Depth First Search,DFS),遍历类似于树的先序通历,是树的先序遍历的推广对于一个连通图,深度优先搜索遍历的过程如下
(1)从图中某个顶点以发,访问”。
(2)找出刚访问过的顶点的第一个未被访问的邻接点,访问该顶点。以该顶点为新顶点,面复此步骤,直至刚访问过的顶点没有未被访同的邻接点为止。
(3)返回前一个访问过的且仍有末被访间的邻接点的顶点,找出该顶点的下一个未被访问的邻接点,访问该顶点。
(4)重复步骤(2)和生骤(3),直至图中所有顶点都被访问过,搜索结束。
第二章 课设实现过程
2.1 深度优先
深度优先算法:
算法实现:深度优先搜索遍历连通图是一个递归的过程。为了在遍历过程中便于区分项点是否己被访问,需附设访问标志数组visited[n],其初值为
“false”或“0”一旦某个顶点被访问,则其相应的分量置为“true”或“1”
2.2 算法实现
采用邻接表表示图的深度优先搜索遍历
【算法描述】
void DFS AL (AIGraph G, int V)
{//图G为邻接表类型,从第,个顶点出发深度优先搜素遍历图G
cout<<v; visited[v]=true;//访问第了个项点,并置访问标志数组相应分量的值为true
p=G.vertices[v].firstarc//0指向口的边链表的第一个边节点
while (p!=NULL) //边节点非空
{
w=p->adjvex; // 表示w是v的邻接点
if (!visited[w]) DFS_AL(G,W);//如果w未访问,则递归调用DES_AL()
p=p->nextarc; //p指向下一个边节点
} //while
}
程序代码
#include <stdio.h>
#include <malloc.h>
#define MAX_SIZE 100
#define M 4 // 行数
#define N 4 // 列数
/*----------------------------以下定义邻接表类型---------------------------*/
typedef struct ANode // 边的结点结构类型
{
int i, j; // 该边的终点位置(i,j)
struct ANode *nextarc; // 指向下一条边的指针
}ArcNode;
typedef struct Vnode // 邻接表头结点的类型
{
ArcNode *firstarc; // 指向第一个相邻点
}VNode;
typedef struct
{
VNode adjlist[M + 2][N + 2]; // 邻接表头结点数组
}ALGraph; // 图的邻接表类型
typedef struct
{
int i; // 当前方块的行号
int j; // 当前方块的列号
}Box;
typedef struct
{
Box data[MAX_SIZE];
int length; // 路径长度
}PathType; // 定义路径类型
int cnt = 0;
int visited[M + 2][N + 2] = {0};
/*-------------------由迷宫数组mg建立对应的邻接表G--------------------*/
static void CreateAdj(ALGraph *&G, int mg[][N + 2])
{
int i, j;
int di;
int i1, j1;
ArcNode *p;
G = (ALGraph *)malloc(sizeof(ALGraph));
for(i = 0; i < M + 2; i++) // 给邻接表中所有头结点的指针域设置初值
{
for(j = 0; j < N + 2; j++)
{
G->adjlist[i][j].firstarc = NULL;
}
}
for(i = 1; i <= M; i++) // 检查mg中每个元素
{
for(j = 1; j <= N; j++)
{
if(mg[i][j] == 0)
{
di = 0;
while(di < 4)
{
switch(di)
{
case 0: // 向上走
i1 = i - 1;
j1 = j;
break;
case 1: // 向右走
i1 = i;
j1 = j + 1;
break;
case 2: // 向下走
i1 = i + 1;
j1 = j;
break;
case 3: // 向左走
i1 = i;
j1 = j - 1;
break;
}
if(mg[i1][j1] == 0) // (i1,j1)为可走方块
{
p = (ArcNode *)malloc(sizeof(ArcNode)); // 创建一个结点p
p->i = i1;
p->j = j1;
p->nextarc = G->adjlist[i][j].firstarc; // 将p结点链到链表后
G->adjlist[i][j].firstarc = p;
}
di++;
}
}
}
}
}
/*-----------------------输出邻接表G--------------------------*/
static void DispAdj(ALGraph *G)
{
int i, j;
ArcNode *p;
for(i = 0; i < M + 2; i++)
{
for(j = 0; j < N + 2; j++)
{
printf(" [%d,%d]: ", i, j);
p = G->adjlist[i][j].firstarc;
while(p != NULL)
{
printf("(%d,%d)", p->i, p->j);
p = p->nextarc;
}
printf("\n");
}
}
}
/*---------------------销毁邻接表--------------------------*/
static void DestroyAdj(ALGraph *&G)
{
int i, j;
ArcNode *pre, *p;
for(i = 0; i < M + 2; i++)
{
for(j = 0; j < N + 2; j++)
{
pre = G->adjlist[i][j].firstarc;
if(pre != NULL)
{
p = pre->nextarc;
while(p != NULL)
{
free(pre);
pre = p;
p = p->nextarc;
}
free(pre);
}
}
}
free(G);
}
/*--------------在图G中采用DFS算法求(xi,yi)到(xe,ye)的所有路径-------------*/
// path数组记录访问过的顶点序列,当找到出口时输出path中的访问序列
static void FindPath(ALGraph *G, int xi, int yi, int xe, int ye, PathType path)
{
ArcNode *p;
visited[xi][yi] = 1; // 置已访问标记
path.data[path.length].i = xi; // 设置起始方块的行号
path.data[path.length].j = yi; // 设置起始方块的列号
path.length++;
if((xi == xe) && (yi == ye)) // 走到迷宫的出口(M,N)时,输出迷宫路径
{
printf(" 迷宫路径%d: ", ++cnt);
for(int k = 0; k < path.length; k++)
{
printf("(%d,%d)", path.data[k].i, path.data[k].j); // 输出迷宫的行号和列号
}
printf("\n");
}
p = G->adjlist[xi][yi].firstarc; //p指向顶点v的第一条边顶点
while(p != NULL)
{
if(visited[p->i][p->j] == 0) // 若(p->i,p->j)方块未访问,则递归访问它
FindPath(G, p->i, p->j, xe, ye, path);
p = p->nextarc; //p指向顶点v的下一条边顶点
}
visited[xi][yi] = 0; // 取消访问标记
}
int main(void)
{
ALGraph *G;
int mg[M + 2][N + 2] = { // 一个迷宫:其四周加上均为1的外框
{1, 1, 1, 1, 1, 1},
{1, 0, 0, 0, 1, 1},
{1, 0, 1, 0, 0, 1},
{1, 0, 0, 0, 1, 1},
{1, 1, 0, 0, 0, 1},
{1, 1, 1, 1, 1, 1}
};
CreateAdj(G, mg);
printf("迷宫对应的邻接表:\n");
DispAdj(G); // 输出邻接表
PathType path;
path.length = 0;
printf("所有的迷宫路径:\n");
FindPath(G, 1, 1, M, N, path);
DestroyAdj(G);
return 0;
}