[1091]. 二進制矩陣中的最短路徑

二進制矩陣中的最短路徑

 


題目

在一個 N × N 的方形網格中,每個單元格有兩種狀態:空(0)或者阻塞(1)

一條從左上角到右下角、長度爲 k 的暢通路徑,由滿足下述條件的單元格 C_1, C_2, ..., C_k 組成:

  • 相鄰單元格 C_iC_{i+1} 在八個方向之一上連通(此時,C_iC_{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求無權圖最短路徑的複雜度:

  • 時間複雜度:Θ(n2)\Theta(n^{2})
  • 空間複雜度:Θ(1)\Theta(1)
     

算法設計:Dijkstra 算法

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章