二進制矩陣中的最短路徑
題目
在一個 N × N
的方形網格中,每個單元格有兩種狀態:空(0)
或者阻塞(1)
。
一條從左上角到右下角、長度爲 k
的暢通路徑,由滿足下述條件的單元格 C_1, C_2, ..., C_k
組成:
-
相鄰單元格
C_i
和C_{i+1}
在八個方向之一上連通(此時,C_i
和C_{i+1}
不同且共享邊或角) -
C_1
位於(0, 0)
(即,值爲grid[0][0]
) -
C_k
位於(N-1, N-1)
(即,值爲grid[N-1][N-1]
) -
如果
C_i
位於(r, c)
,則grid[r][c]
爲空(即,grid[r][c] == 0
)
返回這條從左上角到右下角的最短暢通路徑的長度。如果不存在這樣的路徑,返回 -1
。
示例 1:
輸入:[[0,1],[1,0]]
輸出:2
示例 2:
輸入:[[0,0,0],[1,1,0],[1,1,0]]
輸出:4
函數原型
C的函數原型:
int shortestPathBinaryMatrix(int** grid, int gridSize, int* gridColSize){}
grid
:二維數組gridSize
:二維數組的行數gridColSize
:二維數組的列數,但是int*
,所以取值的時候用*gridColSize
邊界判斷
int shortestPathBinaryMatrix(int** grid, int gridSize, int* gridColSize){
if( grid[0][0] == 1 || grid[gridSize-1][(*gridColSize)-1] == 1) // 起點被堵住 or 終點被堵住
return -1;
if( gridSize = 0 && *gridColSize == 0 )
return 1;
}
算法設計:BFS求無權圖最短路徑
思路:題目是說,在矩陣裏,從左上角
(0,0)
開始到右下角(n-1, n-1)
結束,走出一條最短路徑,剛好BFS
可以求無權圖的最短路徑。
BFS模版:
void BFS()
{
// 定義隊列;
// 建立備忘錄,用於記錄已經訪問的位置;
// 判斷邊界條件,是否能直接返回結果的
// 將起始位置加入到隊列中,同時更新備忘錄
while ( /* 隊列不爲空 */) {
// 獲取當前隊列中的元素個數。
for ( /* 元素個數 */ ) {
// 取出一個位置節點
// 判斷是否到達終點位置
// 獲取它對應的下一個所有的節點
// 條件判斷,過濾掉不符合條件的位置
// 新位置重新加入隊列
}
}
}
完整代碼:
#include<stdbool.h>
#define N 256
int dirs[8][2] = {{-1, 0}, {-1, 1}, {0, 1}, {1, 1},
{1, 0}, {1, -1}, {0, -1}, {-1, -1}};
// 八聯通,按次序{{西}、{西北}、{北}、{東北}、{東}、{東南}、{南}、{西南}}
bool inArea(int x, int y, int R, int C){ // 判斷頂點(x, y)座標是否越界
return x >= 0 && x < R && y >= 0 && y < C;
}
int shortestPathBinaryMatrix(int **grid, int gridSize, int *gridColSize)
{
int R = gridSize;
int C = *gridColSize;
// 這倆個名字太長了,寫到程序裏非常冗餘
/* 建立備忘錄 */
bool visited[N][N] = {0};
int dis[N][N] = {0};
/* 判斷邊界條件 */
if (grid[0][0] == 1)
return -1;
if ((R == 0 && C == 0) || (R == 1 && C == 1))
return 1;
/* 模擬隊列 */
int* pQueue = (int*)malloc(gridSize * (*gridColSize) * sizeof(int));
int front = -1; // 隊尾
int rear = -1; // 隊頭
pQueue[++rear] = 0; // 將第 1 個頂點入隊,從左上角(0,0)開始遍歷
visited[0][0] = true; // 標記爲已訪問
dis[0][0] = 1; // 最短路徑的長度爲 1
while ( front != rear ) // 隊列不爲空
{
int cur = pQueue[++front]; // 出隊
int curx = cur / C, cury = cur % C; // 因爲取出來是一個一維索引,但我們的數組是二維的,所以把一維索引轉爲二維索引
for (int d = 0; d < 8; d++) // 八個方向都要看一下
{
int nextx = curx + dirs[d][0]; // 更新下一個頂點的 x
int nexty = cury + dirs[d][1]; // 更新下一個頂點的 y
// 排除:1、越界 2、已訪問 3、阻塞
if (inArea(nextx, nexty, R, C) && !visited[nextx][nexty] && grid[nextx][nexty] == 0){
pQueue[++rear] = nextx * C + nexty; // 頂點入隊,但因爲 pQueue 存儲的是一維索引,所以要把二維索引轉爲一維索引
visited[nextx][nexty] = true; // 標記爲已訪問
dis[nextx][nexty] = dis[curx][cury] + 1; // 下一個頂點的值 = 原頂點的值 + 1,也就是最短路徑的長度
if (nextx == R - 1 && nexty == C - 1){ // 終於遍歷到了右下角
free(pQueue); pQueue = NULL; // 防止野指針
return dis[nextx][nexty]; // 最短路徑的長度保存在最後一個元素
}
}
}
}
free(pQueue); pQueue = NULL; // 防止野指針
return -1; // 運行到這裏,表示沒找到
}
上面的代碼,有一個小技巧。
- 一維映射二維:
v --> (x, y) --> (v / 列數,v % 列數)
- 二維映射一維:
(x, y) --> v --> x * 列數 + y
BFS
求無權圖最短路徑的複雜度:
- 時間複雜度:
- 空間複雜度: