引言
這是一個簡單的順序棧的應用求解迷宮問題,主要分享的是在求解這個問題的以前的準備,
分析所需的數據,得到正確的數據結構,分析所須要的功能,劃分模塊,再分析各模塊中,須要的具體功能,以肯定功能函數。
這樣也書寫代碼時,就能夠事半功倍。
算法
一,問題描述
迷宮求解問題
提出以一個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。
若是對您有幫助能夠點贊、收藏、關注,將會是我最大的動力 ↩︎