前面我們實現了基礎版本的迷宮求解,只有一條路徑。現在如果有多個出口,我們該如何去找到一條最短的路徑。
我們先來思考一下我們是如何在一個數組裏找最小值的?
有下面一個數組:
我們可以先把第一個數設爲最小值,然後遍歷數組,拿它和後面的元素進行比較,把兩個數中較小的賦給min,直到遍歷完整個數組,min中就是數組中的最小值。
同樣的,找最短路徑也是一樣的思路。我們可以定義兩個棧,一個cur_path保存當前路徑,一個short_path保存最短路徑,每次找到一條路徑,將它保存在cur_path裏,並和short_path進行比較,將兩條路徑中較短的路徑保存在short_path中,最後打印short_path的內容。
可以看出,黃色圈出來的就是最短路徑。
步驟也是和前面大體一致,只是加了一個當前路徑cur_path和最短路徑short_path的比較,代碼中有詳解,大家可以參考一下.
話不多說,直接上代碼:
//先對迷宮初始化
void MazeShortPathInit(Maze* maze)
{
int map[MAX_ROW][MAX_COL] = {
{0,1,0,0,0,0},
{0,1,1,1,0,0},
{0,1,0,1,0,0},
{1,1,0,1,1,1},
{0,1,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 entry)
{
//保存着當前路徑
SeqStack cur_path;
//保存最短路徑
SeqStack short_path;
SeqStackInit(&cur_path);
SeqStackInit(&short_path);
_GetShortPath(maze,entry,entry,&cur_path,&short_path);
//打印棧裏面的內容(通常意義下,棧是不允許遍歷的,但是這裏僅僅用於調試)
SeqStackPrintDebug(&short_path,"最短路徑是:");
}
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);
//3.判斷當前點是否爲出口
if(IsExit(maze,cur,entry))
{
// 如果是出口,說明找到了一條路徑,拿當前路徑和short_path中的路徑比較
// a)如果當前路徑比short_path中路徑短或者short_path爲空,就用當前路徑替換short_path
// b)如果當前路徑沒有short_path中路徑短,嘗試去找其他的路經(進行回溯),出棧cur_path的棧頂元素
printf("找到了一條路徑\n");
if(cur_path->size < short_path->size || short_path->size == 0)
{
SeqStackAssgin(cur_path,short_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);
}
這裏用到的輔助函數打印棧信息SeqStackPrintDebug()和棧替換SeqStackAssgin()如下,而判斷當前點能不能落腳CanStay()函數,判斷是否是出口IsExit()函數和標記Mark()函數在之前博客中(基礎版迷宮求解)寫過,這裏就不做具體介紹。
void SeqStackAssgin(SeqStack* from,SeqStack* to)
{
//爲了保證to裏面的內存能夠足夠的容納from中的元素
//採用以下策略:
//1.釋放to中的原有內存
SeqStackDestroy(to);
//2.根據from中的元素個數,確定內存申請的大小
to->size = from->size;
to->capacity = from->capacity;
//3.給to申請一個足夠的內存
to->data = (SeqStackType*)malloc(to->capacity*sizeof(SeqStackType));
//4.最後進行數據的拷貝
size_t i = 0;
for( ;i < from->size;i++)
{
to->data[i] = from->data[i];
}
return;
}
void SeqStackPrintDebug(SeqStack* stack,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");
}
結果如下圖: