【Leetcode】505. The Maze II

本文介绍如何使用Dijkstra算法解决LeetCode上的迷宫II问题,通过在二维0-1矩阵中寻找从起点到终点的最短路径,详细解析了算法实现步骤与代码,并分析了时间与空间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目地址:

https://leetcode.com/problems/the-maze-ii/

给定一个二维 0 − 1 0-1 01矩阵, 0 0 0代表空地, 1 1 1代表障碍物。在某个空地上有个小球,它可以沿着四个方向滑动,每次滑动时只有遇到边界或者障碍物才会停下来。再给定一个空地作为终点,问该小球是否可能滑到终点,若可能,则返回最短路径的长度(不包括起点,包括终点);若不可能,返回 − 1 -1 1

题目类似于无向带权图的单源最短路问题,所以可以用Dijkstra算法。先将源点加入一个最小堆(按照与源点的路径距离排序),然后将源点出堆,判断其是否已经到了终点,如果到了则直接返回路径长度(Dijkstra算法可以保证出堆的那个点的路径长度就是源点到该点最短路径长度),否则将这个点能到达的点加入最小堆(如果某次从堆中出来的点是 A A A,当算能到达的点的时候,将这些点的路径长度设为这个点与 A A A的距离加上源点与 A A A的距离,即两段距离的和。另外注意,由于算法保证了出堆的点对应的距离就是最短路的距离,所以要标记其为已访问过,以后再遇到这个点的时候就直接跳过),加入堆后再重复上述过程即可。

代码如下:

class Solution {
 public:
  using PIII = pair<int, pair<int, int>>;
#define x first
#define y second
  const int INF = 1e9;
  int shortestDistance(vector<vector<int>>& g, vector<int>& start,
                       vector<int>& dest) {
    int m = g.size(), n = g[0].size();
    priority_queue<PIII, vector<PIII>, greater<>> heap;
    int bx = start[0], by = start[1], ex = dest[0], ey = dest[1];
    heap.push({0, {bx, by}});
    vector<vector<int>> dist(m, vector<int>(n, INF));
    dist[bx][by] = 0;
    vector<vector<bool>> vis(m, vector<bool>(n, false));
    // 得到从(x, y)出发能滚到的位置
    auto get_ne = [&](int x, int y) {
      vector<PIII> res;
      static int d[] = {-1, 0, 1, 0, -1};
      for (int i = 0; i < 4; i++) {
        int dx = d[i], dy = d[i + 1];
        int delta;
        for (delta = 1;; delta++) {
          int nx = x + delta * dx, ny = y + delta * dy;
          if (0 <= nx && nx < m && 0 <= ny && ny < n && !g[nx][ny]) continue;
          else break;
        }
        // 略过前进不了的方向
        if (delta == 1) continue;
        int nx = x + (delta - 1) * dx, ny = y + (delta - 1) * dy;
        // 略过最短路已经算出来过的点
        if (vis[nx][ny]) continue;
        res.push_back({delta - 1, {nx, ny}});
      }

      return res;
    };

    while (heap.size()) {
      auto [d, cur] = heap.top(); heap.pop();
      auto [x, y] = cur;
      if (vis[x][y]) continue;
      if (x == ex && y == ey) return d;
      vis[x][y] = true;
      dist[x][y] = d;

      for (auto [dis, ne] : get_ne(x, y)) {
        auto [nx, ny] = ne;
        if (d + dis < dist[nx][ny]) {
          dist[nx][ny] = d + dis;
          heap.push({dist[nx][ny], {nx, ny}});
        }
      }
    }

    return -1;
  }
};

时间复杂度 O ( m n log ⁡ ( m n ) ) O(mn\log (mn)) O(mnlog(mn)),空间 O ( m n ) O(mn) O(mn)

需要注意:
if (vis[x][y]) continue;这句话是必要的。它不影响正确性,但会提高效率。虽然在get_ne这个方法里,返回的是未算过最短路的点,但是仍然有可能某个点已经在堆中未算过最短路,然后在get_ne里又加入了堆里。这时等到这个点算过最短路后,后面这个点可能又出堆一次,而这一次需要略过,即用continue跳过下面的部分。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值