引言
这是一个简单的顺序栈的应用求解迷宫问题,主要分享的是在求解这个问题的以前的准备,
分析所需的数据,得到正确的数据结构,分析所须要的功能,划分模块,再分析各模块中,须要的具体功能,以肯定功能函数。
这样也书写代码时,就能够事半功倍。
算法
一,问题描述
迷宫求解问题
提出以一个m*n的长方阵表示迷宫,0和1分别表示迷宫中的通路和障碍。迷宮问题要求,求出从入口(x,y)到出口(x,y)的一条通路,或得出没有通路的结论。
基本要求:首先实现一个以链表做存储结构的栈类型,而后编写一个求迷宫问题的非递归程序,求得的通路。
要求用栈实现迷宫问题的求解
数组
将要构建的迷宫:向下为x正方向;向右为y正方向数据结构
二,分析所用数据结构
迷宫结构体用于存储构建的迷宫数据。
坐标结构体和栈元素结构体都是服务于栈结构体。
函数
3、所需函数及其功能
这幅图能够很清晰的,了解都有哪些函数,这些函数的功能又是什么。
图中不少函数都是为了一个函数服务的,即求解迷宫的函数。
蓝色底的函数,实现了可是没有测试,你们能够自行测试
测试
4、程序执行详细框图
这是整个迷宫问题项目的详细执行过程。你们能够先看看,到时候阅读代码也会更加清洗直观。
不一样的颜色,是一个不一样的模块,实现相应的功能
优化
5、代码实现-详细注释
代码相应的地方都有注释,也体现了我思考的过程,若有错误或者更优解,欢迎在指正讨论。spa
一、maze.h
#ifndef __MAZE_H__ #define __MAZE_H__ #include <stdio.h> #include <stdlib.h> #include <stdbool.h> #define TRUE 1 #define FALSE 0 #define STACK_INIT_SIZE 100 #define STACKINCREMENT 10 #define COLUMN 10 //列 #define ROW 10 //行 typedef struct{ char** maze; //迷宫二维数组 int** footprint; //足迹二维数组 int row; int column; }MazeType; typedef struct{ int x; int y; }PosType; typedef struct{ int ord; //通道块在路径上的序号 PosType seat; //通道块在迷宫中的“坐标位置” int di; //今后通道块走向下一个通道块的“方向” }SElemType; typedef struct{ SElemType* base; SElemType* top; int stacksize; }SqStack; //构造一个空栈 bool InitStack(SqStack* S); //初始化迷宫数据 bool InitMaze(MazeType* M); //判断是否为空栈 bool IsStackEmpty(SqStack S); //入栈,元素e为新的栈顶元素,传入e形参拷贝值,返回改变的栈S,及是否入栈成功 bool Push(SqStack* S, SElemType e); //出栈,指针传入地址,直接改变e变量,即返回改变的e和栈S,及是否出栈成功 bool Pop(SqStack* S, SElemType* e); //输出迷宫 bool PrintfMaze(MazeType* M); //输出迷宫的路径 bool PrintfFoot(MazeType* M, SqStack* S); //将迷宫的当前位置Pos设置为“走过”,即footprint该位置为1 bool FootPrint(MazeType* M, PosType pos); //判断当前位置是否走过 bool Pass(MazeType* M, PosType pos); //建立新的节点,用step,pos,d初始化该点 SElemType NewSElemType(int step, PosType pos, int d); //将位置pos的方向设为d PosType NextPos(PosType pos, int d); //若迷宫maze中存在从入口start到出口end的通道,则求得一条存放在栈中(从栈底到栈顶) bool MazePath(SqStack* S, MazeType maze, PosType start, PosType end); //清空栈 bool ClearStack(SqStack* S); //从栈底到栈顶依次对每一个元素进行访问 bool StackTravel(const SqStack* S); //返回栈的长度,即S元素的个数 int StackLength(SqStack S); //若栈不为空,则用e返回S的栈顶元素 bool GetTop(SqStack S, SElemType* e); #endif
二、maze.c
#include "maze.h" //构造一个空栈 bool InitStack(SqStack* S) { //100*SElemType S->base = (SElemType*)malloc(STACK_INIT_SIZE*sizeof(SElemType)); if(!S->base) { printf("申请空间失败,迷宫没法初始化.\n"); return false; } S->top = S->base; S->stacksize = STACK_INIT_SIZE; return true; } //初始化迷宫数据 bool InitMaze(MazeType* M) { char mz[ROW][COLUMN]={ { '#',' ','#','#','#','#','#','#','#','#'}, { '#',' ',' ','#',' ',' ',' ','#',' ','#'}, { '#',' ',' ','#',' ',' ',' ','#',' ','#'}, { '#',' ',' ',' ',' ','#','#',' ',' ','#'}, { '#',' ','#','#','#',' ',' ',' ',' ','#'}, { '#',' ',' ',' ','#',' ','#',' ','#','#'}, { '#',' ','#',' ',' ',' ','#',' ',' ','#'}, { '#',' ','#','#','#',' ','#','#',' ','#'}, { '#','#',' ',' ',' ',' ',' ',' ',' ',' '}, { '#','#','#','#','#','#','#','#','#','#'}, }; M->maze = (char **)malloc(sizeof(char*)*ROW); //至关于分配一维数组空间,10个char*变量空间 M->footprint = (int **)malloc(sizeof(int*)*ROW); //至关于分配一维数组空间,10个int*变量空间 if(!M->maze || !M->footprint) { printf("申请空间失败,迷宫没法初始化.\n"); return false; } for(int i = 0; i < ROW; i++) { M->maze[i]=(char*)malloc(sizeof(char)*COLUMN); //至关于分配二维数组空间,每一个个char*指向,10个char大小变量空间 M->footprint[i]=(int*)malloc(sizeof(int)*COLUMN); //至关于分配二维数组空间,每一个个int*指向,10个int大小变量空间 if(!M->maze[i] || !M->footprint[i]) { printf("申请空间失败,迷宫没法初始化.\n"); return false; } } for(int i = 0; i <ROW; i++) { for(int j = 0; j < COLUMN; j++) { M->maze[i][j] = mz[i][j]; M->footprint[i][j] = 0; } } M->row = ROW; M->column = COLUMN; return true; } //判断是否为空帐 bool IsStackEmpty(SqStack S) { if(S.top == S.base) return true; else return false; } //入栈,元素e为新的栈顶元素,传入e形参拷贝值,返回改变的栈S,及是否入栈成功 bool Push(SqStack* S, SElemType e) { //结构体类型,按单位大小相减类比int型,每一个int型为4byte,相减2-1也是按斯单位相减 if(S->top - S->base >= S->stacksize) //若是超出原本的长度,进行动态的添加,每一次添加10个SElemType大小空间,STACKINCREMENT=10 { S->base = (SElemType*)realloc(S->base,(S->stacksize + STACKINCREMENT)*sizeof(SElemType)); if(!S->base); { printf("从新申请空间失败.\n"); return false; } S->top = S->base + S->stacksize; //栈顶指针指向原先栈的尾部,栈底+栈长度 S->stacksize +=STACKINCREMENT; //栈的长度+10 } *S->top++=e; //栈顶指针+1前进,而且e赋值给解引用的指针,入栈 //后置++/--为第一优先级,*和前置++/--为第二优先级 return true; } //出栈,指针传入地址,直接改变e变量,即返回改变的e和栈S,及是否出栈成功 bool Pop(SqStack* S, SElemType* e) { if(S->top == S->base) { printf("栈为空.\n"); return false; } *e = *(--S->top); //栈顶指针-1返回,而且解引用赋值给e return true; } //输出迷宫 bool PrintfMaze(MazeType* M) { printf("%s","xy"); for(int i=0;i<M->column;i++) { printf("%d",i); } printf("\n"); for(int i=0; i<M->row; i++) { printf("%d ",i); for(int j=0; j<M->column; j++) { printf("%c",M->maze[i][j]); } printf("\n"); } printf("\n"); //footprintf printf("%s","xy"); for(int i=0;i<M->column;i++) { printf("%d",i); } printf("\n"); for(int i=0; i<M->row; i++) { printf("%d ",i); for(int j=0; j<M->column; j++) { printf("%d",M->footprint[i][j]); } printf("\n"); } printf("\n"); return true; } //输出迷宫路径 bool PrintfFoot(MazeType* M, SqStack* S) { SElemType* p; for(int i=0; i<M->row; i++) //将footprint置0 { for(int j=0; j<M->column; j++) { M->footprint[i][j]=0; } } p = S->base; if(S->base == S->top) { printf("栈为空.\n"); return false; } while(p != S->top) //根据栈中存有的节点的坐标,对footprint进行1路径赋值 { M->footprint[p->seat.x][p->seat.y] = 1; *p++; } for(int i=0; i<M->row; i++) //输出路径 { for(int j=0; j<M->column; j++) { printf("%d",M->footprint[i][j]); } printf("\n"); } return true; } //将迷宫的当前位置Pos设置为“走过”,即footprint该位置为1 bool FootPrint(MazeType* M, PosType pos) //FootPrint足迹 { if((pos.x>M->row) || (pos.y>M->column)) { printf("坐标越界.\n"); return false; } M->footprint[pos.x][pos.y]=1; return true; } //判断当前位置是否走过 bool Pass(MazeType* M, PosType pos) { if((pos.x > M->row) || (pos.y > M->column)) { printf("坐标越界.\n"); return false; } if((0 == M->footprint[pos.x][pos.y])&&(M->maze[pos.x][pos.y]==' ')) return true; //通路没走过 else return false; //通路走过或者墙 } //建立新的节点,用step,pos,d初始化该点 SElemType NewSElemType(int step, PosType pos, int d) { SElemType e; e.ord = step; e.seat = pos; e.di = d; return e; } //将位置pos的方向设为d PosType NextPos(PosType pos, int d) { switch(d) { case 1: //向下 pos.x++; break; case 2: //向右 pos.y++; break; case 3: //向上 pos.x--; break; case 4: //向左 pos.y--; break; default: printf("error.\n"); } return pos; } //若迷宫maze中存在从入口start到出口end的通道,则求得一条存放在栈中(从栈底到栈顶) bool MazePath(SqStack* S, MazeType maze, PosType start, PosType end) { int curstep = 1; SElemType e; PosType curpos = start; InitStack(S); do { if(true == Pass(&maze, curpos)) //通路没走过 { FootPrint(&maze,curpos);将当前点标记为走过 e=NewSElemType(curstep,curpos,1);//建立栈元素,将当前点的信息存储,便于出入栈及改变当前点的di信息 Push(S,e); if((curpos.x==end.x)&&(curpos.y==end.y))//终点 { printf("迷宫路径:\n"); PrintfFoot(&maze,S);//打印通路路径 return true; } curpos = NextPos(curpos,1);//向当前点的di方向偏移 ,即下一个点 curstep++;//步数+1 } else //通路走过或者墙 { if(!IsStackEmpty(*S)) { Pop(S,&e); while(e.di==4 && !IsStackEmpty(*S)) //四个方向遍历完,说明此点不是正确道路,,且且栈不为空,就一直出栈 { Pop(S,&e); } if(e.di<4) { e.di++;//改变当前点的di,偏移方向 Push(S,e);//将改变了di的当前点信息,入栈 curpos=NextPos(e.seat,e.di);//向当前点的di方向偏移 } } } }while(!IsStackEmpty(*S)); return false; } //清空栈 bool ClearStack(SqStack* S) { //把栈S置为空栈 if(!S) return false; S->top = S->base; return true; } //从栈底到栈顶依次对每一个元素进行访问 bool StackTravel(const SqStack* S) { SElemType* p = S->base; if(S->base == S->top) { printf("栈为空.\n"); return false; } printf("栈中元素:\n"); while(p != S->top) { printf("x=%d,y=%d\n",p->seat.x,p->seat.y); *p++; } printf("\n"); return true; } //返回栈的长度,即S元素的个数 int StackLength(SqStack S) { return S.stacksize; } //若栈不为空,则用e返回S的栈顶元素 bool GetTop(SqStack S, SElemType* e) { if(S.top == S.base) { printf("栈为空.\n"); return false; } else { *e = *(S.top - 1); //printf("栈顶元素:%c\n",*e); return true; } }
三、maze.c
#include "maze.h" int main() { MazeType maze; SqStack *stack =(SqStack *)malloc(sizeof(SqStack)); PosType start,end; start.x = 1; start.y = 6; end.x = 8; end.y = 9; InitMaze(&maze); printf("maze:\n"); PrintfMaze(&maze); if(true == MazePath(stack,maze,start,end)) printf("maze can out.\n"); else printf("maze can not out.\n"); StackTravel(stack); printf("栈的长度:%d\n",StackLength(*stack)); SElemType ele; GetTop(*stack,&ele); printf("栈顶元素:ord=%d,x=%d,y=%d,di=%d\n",ele.ord,ele.seat.x,ele.seat.y,ele.di); ClearStack(stack); GetTop(*stack,&ele); //销毁栈——销毁free空间,须要在同一个内存空间销毁 free(stack); stack=NULL; return 0; }
六,效果展现
此测试起点(1,6),终点(8,9)3d
相信你们看到这个结果是会有疑问的,由于虽然获得一条正确的迷宫路径,但却不是最优解,这是为何呢?指针
依旧存在缺陷的迷宫算法,没法得到最优路径解,由于遍历的方向固定,因此你们有什么办法能够优化它呢?code
若有不足之处,还望指正 1。
若是对您有帮助能够点赞、收藏、关注,将会是我最大的动力 ↩︎