如圖是一個有三個出口的迷宮,現在來求它的最短路徑(不帶環)
思想:定義兩個棧,一個棧存放當前路徑。一個棧存最短路徑,每次都將當前路徑和最短路徑比較如果當前路徑小於最短路徑,那就交換兩個棧中的元素
代碼實現:
void MazeInit2(Maze* maze)
{
if(maze == NULL)
{
return;
}
int map[MAX_ROW][MAX_COL] = {
{0,1,0,0,0,0},
{0,1,1,1,0,0},
{0,1,0,1,1,1},
{1,1,1,0,0,0},
{0,0,1,0,0,0},
{0,0,1,0,0,0}
};
size_t i = 0;
for(;i < MAX_ROW;++i)
{
size_t j = 0;
for(;j < MAX_COL;++j)
{
maze->map[i][j] = map[i][j];
}
}
return;
}
void _GetShortPath(Maze* maze,Point cur,Point entry,SeqStack* cur_path,SeqStack* short_path)
{
//1.判斷當前是否能落腳
if(!CanStay(maze,cur))
{
return;
}
//2.如果能落腳,就對當前點進行標記,同時把當前點插入到cur_path
Mark(maze,cur);
SeqStackPush(cur_path,cur);
//判斷當前點是否是出口
if(IsExit(maze,cur,entry))
{
//a)如果是出口,說明找到了一條路徑,拿當前路徑和short_path中的路徑進行對比
//,如果當前路徑比short_path短,或short_path本身是一個空棧
// 就用當前路徑代替short_path
printf("找到一條路經!\n");
if(cur_path->size < short_path->size || short_path->size == 0)
{
printf("找到一條比較短的路徑!\n");
SeqStackAssgin(cur_path,short_path);
//printf("cur :%d,%d\n",cur.row,cur.col);
}
//b)如果當前路徑沒有short_path短,就繼續再找其他路徑(進行回溯)
// 回溯之前要把cur_path棧頂元素出棧
SeqStackPop(cur_path);
return;
}
//4.如果當前點不是出口,嘗試探測四個方向(按照順時針的方向探測)
Point up = cur;
up.row -= 1;
_GetShortPath(maze,up,entry,cur_path,short_path);
Point right = cur;
right.col += 1;
_GetShortPath(maze,right,entry,cur_path,short_path);
Point down = cur;
down.row += 1;
_GetShortPath(maze,down,entry,cur_path,short_path);
Point left = cur;
left.col -= 1;
_GetShortPath(maze,left,entry,cur_path,short_path);
//5.如果四個方向都探測過了,就可以進行出棧(指當前函數棧幀結束
//,同時cur_path,也要進行出棧),回溯到上一個點
SeqStackPop(cur_path);
return;
}
//找到所有路徑,然後從所有的路徑中篩選出一條最短的(遞歸)
void GetShortPath(Maze* maze,Point entry)
{
//就像從一個數組中查找最小元素,一樣,定義一個short_path保存最短路徑
SeqStack cur_path;//保存當前路徑
SeqStack short_path;//保存最短路徑
SeqStackInit(&cur_path);
SeqStackInit(&short_path);
_GetShortPath(maze,entry,entry,&cur_path,&short_path);
SeqStackDebugPrint(&short_path,"最短路徑");
}
seqstack.c中的代碼實現
#ifdef FOR_MAZE
//#include<stdio.h>
//#include"seqstack.h"
//此函數僅用於迷宮問題,用來調試
//通常意義下,棧是不允許遍歷的
//但是如果進行調試或者測試,就不一樣了
//因此再這裏雖然函數進行遍歷了,但是僅用於調試
//之所以寫這個函數遍歷棧,是爲了能夠從入口到出口的順序來打印棧的內容
void SeqStackDebugPrint(SeqStack* stack,const char* msg)
{
printf("[%s]\n",msg);
size_t i = 0;
for(;i < stack->size ; ++i)
{
printf("(%d,%d)\n",stack->data[i].row,stack->data[i].col);
}
printf("\n");
}
void SeqStackAssgin(SeqStack* from,SeqStack* to)
{
//採用下面的方法保證to裏面的內存能夠足夠容納from中的元素
//1.釋放to中的原有內存
SeqStackDestroy(to);
//2.根據from元素的個數,確定內存申請內存的大小,給to重新申請一個足夠的內存
to->size = from->size;
to->capacity = from->capacity;
to->data = (SeqStackType*)malloc(to->capacity * sizeof(SeqStackType));
//3.進行數據拷貝
size_t i = 0;
for(;i < from->size ; ++i)
{
to->data[i] = from->data[i];
}
return;
}
#endif
測試代碼
void Test3()
{
TEST_HEADER;
Maze maze;
MazeInit2(&maze);
Point entry = {0,1};
GetShortPath(&maze,entry);
MazePrint(&maze);
}
運行結果:
如圖是一個有三個出口的迷宮,現在來求它的最短路徑(帶環)
因爲這個迷宮帶環路徑較多就不一一畫出來了
第一次標記的結果
第二次標記的結果
第三次標記的結果
第四次標記結果
第五次標記結果
具體看代碼實現
void MazeInit3(Maze* maze)
{
if(maze == NULL)
{
return;
}
int map[MAX_ROW][MAX_COL] = {
{0,1,0,0,0,0},
{0,1,1,1,0,0},
{0,1,0,1,1,1},
{1,1,1,1,0,0},
{0,0,1,0,0,0},
{0,0,1,0,0,0}
};
size_t i = 0;
for(;i < MAX_ROW;++i)
{
size_t j = 0;
for(;j < MAX_COL;++j)
{
maze->map[i][j] = map[i][j];
}
}
return;
}
int CanStayWithCycle(Maze *maze,Point cur,Point pre)
{
if(maze == NULL)
{
return 0;
}
//當前點是否在地圖上
if(cur.row < 0 || cur.row >= MAX_ROW || cur.col < 0 || cur.col >= MAX_COL)
{
return 0;
}
//當前點不是牆
int cur_value = maze->map[cur.row][cur.col];
if(cur_value == 0)
{
return 0;
}
//如果當前點是1,就可以直接落腳
if(cur_value == 1)
{
return 1;
}
//當前點若已經被標記就比較當前點對應的值和pre對應的值的大小
//a)cur_path 7,pre_value 5,應該落腳
//b)cur_path 6,pre_value 5,不應該落腳
//c)cur_path 5,pre_value 5,不應該落腳
//d)cur_path 4,pre_value 5,不應該落腳
//總結以上的幾種情況可以總結出:cur_path > pre_path + 1就應該落腳
int pre_value = maze->map[pre.row][pre.col];
if(cur_value > pre_value+1)
{
return 1;
}
return 0;
}
void MarkWithCycle(Maze* maze,Point cur,Point pre)
{
if(maze == NULL)
{
return;
}
if(cur.row < 0 || cur.row >= MAX_ROW || cur.col < 0 || cur.col >= MAX_COL)
{
return;
}
// if(pre.row < 0 || pre.row >= MAX_ROW || pre.col < 0 || pre.col >= MAX_COL)
// {
// return;
// }
if(pre.row == -1 && pre.col == -1)
{
maze->map[cur.row][cur.col] = 2;
return;
}
int pre_value = maze->map[pre.row][pre.col];
maze->map[cur.row][cur.col] = pre_value + 1;
return;
}
void _GetShortPathWithCycle(Maze* maze,Point cur,Point pre,Point entry,SeqStack* cur_path,SeqStack* short_path)
{
//判定當前是否能落腳(判定規則變了)
printf("cur :(%d,%d)\n",cur.row,cur.col);
if(!CanStayWithCycle(maze,cur,pre))
{
return;
}
//標記當前點,並且將當前點插入到cur_path
MarkWithCycle(maze,cur,pre);
SeqStackPush(cur_path,cur);
pre = cur;
//判斷當前點是否是出口
if(IsExit(maze,cur,entry))
{
printf("找到了一條路徑!\n");
//如果是出口,要拿cur_path和short_path進行比較,把比較短的路徑保存到short_path中
if(cur_path->size < short_path->size || short_path->size == 0)
{
printf("找到了一條比較短的路徑!\n");
SeqStackAssgin(cur_path,short_path);
}
//進行回溯,不管當前找到的這條路徑是否是比較短的路徑,都要進行回溯
SeqStackPop(cur_path);
return;
}
//如果不是出口,以當前點爲基準點,探測周圍的四個點
Point up = cur;
up.row -= 1;
_GetShortPathWithCycle(maze,up,pre,entry,cur_path,short_path);
Point right = cur;
right.col += 1;
_GetShortPathWithCycle(maze,right,pre,entry,cur_path,short_path);
Point down = cur;
down.row += 1;
_GetShortPathWithCycle(maze,down,pre,entry,cur_path,short_path);
Point left = cur;
left.col -= 1;
_GetShortPathWithCycle(maze,left,pre,entry,cur_path,short_path);
//如果四個點都探測過了,就出棧回溯
SeqStackPop(cur_path);
return;
}
void GetShortPathWithCycle(Maze* maze,Point entry)
{
SeqStack cur_path;
SeqStack short_path;
SeqStackInit(&cur_path);
SeqStackInit(&short_path);
Point pre = {-1,-1};
_GetShortPathWithCycle(maze,entry,pre,entry,&cur_path,&short_path);
SeqStackDebugPrint(&short_path,"最短路徑爲");
}
測試代碼
void Test4()
{
TEST_HEADER;
Maze maze;
MazeInit3(&maze);
Point entry = {0,1};
GetShortPathWithCycle(&maze,entry);
MazePrint(&maze);
}
運行結果