二维网格路径可达性判断:是否存在快于BFS的高效解法?
针对网格路径判断的BFS优化方案
你的问题核心是标准BFS在大规模网格下的超时问题,本质是不必要的节点遍历导致的。以下是几个能显著提升效率的优化方向:
1. 提前终止与基础剪枝
- 理论最短路径预判:先计算起点到终点的曼哈顿距离(
(rows-1)+(cols-1)),如果这个最短路径长度已经大于等于给定的最大值,直接返回false——因为不存在更短的路径了。 - 到达终点即返回:BFS过程中,只要第一次到达右下角节点,立刻判断当前路径长度+1(到终点的步数)是否小于最大值,满足就直接返回
true,无需继续遍历其他节点。 - 无效节点跳过:如果队列中当前节点的路径长度已经大于等于最大值,直接终止整个BFS,因为后续节点的路径只会更长。
2. 访问标记优化
- 原地修改网格:如果题目允许修改输入网格,直接将已访问的
'o'改为'x',省掉额外的访问矩阵空间,同时减少判断时的内存访问开销。 - 最短路径记录剪枝:如果不能修改原网格,用二维数组记录每个节点的最短路径长度。当新路径长度大于等于已记录的长度时,直接跳过该节点——BFS的层级遍历特性保证先到达的路径一定是最短的,更长的路径没有扩展价值。
3. 曼哈顿距离剪枝
遍历每个节点的邻居时,计算邻居到终点的曼哈顿距离,若当前路径长度+1 + 曼哈顿距离 >= 最大值,则这个邻居无需加入队列——即使走最短路径到终点,总长度也不满足要求,扩展它纯属浪费资源。
4. 双向BFS(最适合大规模网格)
从起点和终点同时启动BFS,每次选择节点数更少的队列进行扩展,当两个搜索的区域相遇时,计算两段路径的总长度:
- 若总长度小于最大值,直接返回
true; - 若其中一个队列为空,说明路径不可达,返回
false。
这种方式能将搜索空间从单向的O(N)压缩到O(2*sqrt(N)),在大网格下效率提升非常明显。
优化后的BFS示例代码(含剪枝)
def is_path_valid(max_length, grid): rows = len(grid) cols = len(grid[0]) if rows else 0 if rows == 0 or cols == 0: return False start, end = (0, 0), (rows-1, cols-1) # 预判最短路径 min_possible = (rows-1) + (cols-1) if min_possible >= max_length: return False from collections import deque q = deque([(start[0], start[1], 0)]) # 原地标记已访问 grid[0][0] = 'x' directions = [(-1,0), (1,0), (0,-1), (0,1)] while q: x, y, dist = q.popleft() # 当前路径长度已达标,后续只会更长,直接终止 if dist >= max_length - 1: return False for dx, dy in directions: nx, ny = x + dx, y + dy # 到达终点,直接判断 if nx == end[0] and ny == end[1]: return (dist + 1) < max_length # 边界与可通行判断 if 0 <= nx < rows and 0 <= ny < cols and grid[nx][ny] == 'o': # 曼哈顿剪枝 manhattan = (end[0] - nx) + (end[1] - ny) if (dist + 1 + manhattan) >= max_length: continue grid[nx][ny] = 'x' q.append((nx, ny, dist + 1)) # 路径不可达 return False
双向BFS示例代码
def is_path_valid(max_length, grid): rows = len(grid) cols = len(grid[0]) if rows else 0 if rows == 0 or cols == 0: return False start, end = (0, 0), (rows-1, cols-1) min_possible = (rows-1) + (cols-1) if min_possible >= max_length: return False from collections import deque # 双向队列与距离记录 q_start = deque([(start[0], start[1], 0)]) q_end = deque([(end[0], end[1], 0)]) dist_start = [[-1]*cols for _ in range(rows)] dist_end = [[-1]*cols for _ in range(rows)] dist_start[0][0] = 0 dist_end[rows-1][cols-1] = 0 directions = [(-1,0), (1,0), (0,-1), (0,1)] while q_start and q_end: # 优先扩展节点数少的队列,减少搜索空间 if len(q_start) <= len(q_end): x, y, d = q_start.popleft() for dx, dy in directions: nx, ny = x+dx, y+dy if 0<=nx<rows and 0<=ny<cols and grid[nx][ny]=='o' and dist_start[nx][ny]==-1: # 两个搜索相遇,计算总长度 if dist_end[nx][ny] != -1: return (d+1 + dist_end[nx][ny]) < max_length dist_start[nx][ny] = d+1 # 剪枝:剩余最短路径已不满足要求,跳过 manhattan = (end[0]-nx)+(end[1]-ny) if (d+1 + manhattan) < max_length: q_start.append((nx, ny, d+1)) else: x, y, d = q_end.popleft() for dx, dy in directions: nx, ny = x+dx, y+dy if 0<=nx<rows and 0<=ny<cols and grid[nx][ny]=='o' and dist_end[nx][ny]==-1: if dist_start[nx][ny] != -1: return (dist_start[nx][ny] + d+1) < max_length dist_end[nx][ny] = d+1 manhattan = (nx - start[0]) + (ny - start[1]) if (d+1 + manhattan) < max_length: q_end.append((nx, ny, d+1)) return False
内容的提问来源于stack exchange,提问作者Ryan Erny
相关产品推荐
相关产品推荐

