二进制矩阵中的最短路径
-
题目
-
函数原型
-
边界判断
-
算法设计:BFS求无权图最短路径
-
算法设计:Dijkstra 算法
题目
在一个
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:
输出:
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
求无权图最短路径的复杂度:
-
时间复杂度:
-
空间复杂度:
算法设计:Dijkstra 算法