相关文章推荐
深沉的山羊  ·  遇到:parent.relativePath ...·  5 月前    · 
孤独的钢笔  ·  WINDOW function (DAX) ...·  1 年前    · 
精明的手套  ·  DRF + jwt + mysql ...·  1 年前    · 


二进制矩阵中的最短路径

  • 题目
  • 函数原型
  • 边界判断
  • 算法设计: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:

输入:[[0,1],[1,0]]

[1091]. 二进制矩阵中的最短路径_二维


输出: 2

[1091]. 二进制矩阵中的最短路径_算法设计_02




示例 2:

输入:[[0,0,0],[1,1,0],[1,1,0]]

[1091]. 二进制矩阵中的最短路径_算法设计_03


输出: 4

[1091]. 二进制矩阵中的最短路径_算法设计_04


函数原型

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 求无权图最短路径的复杂度:

  • 时间复杂度: [1091]. 二进制矩阵中的最短路径_最短路径_05
  • 空间复杂度: [1091]. 二进制矩阵中的最短路径_二维_06

算法设计:Dijkstra 算法