1 题目
题目:骑士的最短路线(Knight Shortest Path)
描述:给定骑士在棋盘上的 初始 位置(一个2进制矩阵 0 表示空 1 表示有障碍物),找到到达 终点 的最短路线,返回路线的长度。如果骑士不能到达则返回 -1 。
-
起点跟终点必定为空.
-
骑士不能碰到障碍物.
-
路径长度指骑士走的步数.
-
如果骑士的位置为 (x,y),他下一步可以到达以下这些位置:
(x + 1, y + 2) (x + 1, y - 2) (x - 1, y + 2) (x - 1, y - 2) (x + 2, y + 1) (x + 2, y - 1) (x - 2, y + 1) (x - 2, y - 1)
lintcode题号——611,难度——medium
样例1:
输入:
[[0,0,0],
[0,0,0],
[0,0,0]]
source = [2, 0] destination = [2, 2]
输出: 2
解释:
[2,0]->[0,1]->[2,2]
样例2:
输入:
[[0,1,0],
[0,0,1],
[0,0,0]]
source = [2, 0] destination = [2, 2]
输出:-1
2 解决方案
2.1 思路
找最短路径,使用宽度优先搜索,由于要记录步数,所以搜索过程中需要按层搜索,每层即是一步,每次宽搜都找到下一步能够到达的位置,需要对已经走过的位置进行记录,防止一直绕圈。
2.2 时间复杂度
图上的宽度优先搜索,算法的时间复杂度为O(n+m),其中n为节点数,m为边数。R行C列的矩阵,可以看成有R*C
个节点和R*C*2
条边的图,在其上进行BFS,时间复杂度为O(R*C)
。
2.3 空间复杂度
图上的宽度优先搜索,使用了queue队列数据结构,队列最大容量为节点数,即R*C
,算法的空间复杂度为O(R*C)
。
3 源码
细节:
- 使用宽度优先搜索。
- 由于要记录步数,所以需要进行层级遍历,进入循环时记录queue的大小,每次循环完一轮表示一步。
- 需要对已经走过的位置进行记录,防止走回之前的节点。
- 由于pair没有内置的hash函数,所以无法被unordered_set包装,可以使用set来包装。
- 如果下一步即是终点,返回的步数需要加1。
C++版本:
/**
* Definition for a point.
* struct Point {
* int x;
* int y;
* Point() : x(0), y(0) {}
* Point(int a, int b) : x(a), y(b) {}
* };
*/
/**
* @param grid: a chessboard included 0 (false) and 1 (true)
* @param source: a point
* @param destination: a point
* @return: the shortest path
*/
int shortestPath(vector<vector<bool>> &grid, Point source, Point destination) {
// write your code here
int result = 0;
if (grid.empty() || grid.front().empty())
{
return result;
}
if (source.x == destination.x && source.y == destination.y)
{
return result;
}
queue<pair<int, int>> posQueue;
set<pair<int, int>> posSet; // 用于记录已经走过的位置,由于pair没有内置hash函数,所以不能用unordered_set装pair
posQueue.push({source.x, source.y});
posSet.insert({source.x, source.y});
int a[] = {1, 1, -1, -1, 2, 2, -2, -2};
int b[] = {2, -2, 2, -2, 1, -1, 1, -1};
while (!posQueue.empty())
{
int size = posQueue.size();
for (int i = 0; i < size; i++)
{
int row = posQueue.front().first;
int col = posQueue.front().second;
posQueue.pop();
for (int j = 0; j < 8; j++)
{
int newRow = row + a[j];
int newCol = col + b[j];
// 跳出棋盘无效
if (!isValid(grid, newRow, newCol))
{
continue;
}
// 跳到了终点
if (newRow == destination.x && newCol == destination.y)
{
return result + 1; // 下一步就是终点
}
// 如果该位置没有障碍,且没有到过
if (grid.at(newRow).at(newCol) == 0
&& posSet.find(make_pair(newRow, newCol)) == posSet.end())
{
posQueue.push({newRow, newCol});
posSet.insert(make_pair(newRow, newCol));
}
}
}
result++;
}
return -1;
}
bool isValid(vector<vector<bool>> & grid, int row, int col)
{
int maxRow = grid.size() - 1;
int maxCol = grid.front().size() - 1;
if (row < 0 || row > maxRow)
{
return false;
}
if (col < 0 || col > maxCol)
{
return false;
}
return true;
}