如何基于Python实现支持可逆使用的网格严格递增路径查找
解决网格中从左上到右下的严格递增路径问题
嘿,我来帮你搞定这个问题!刚好之前做过类似的路径查找需求,下面给你两种实用的Python解决方案,都支持可逆使用(也就是可以重复调用,处理不同网格或者同一网格多次),完全符合你的要求。
首先先明确下核心规则:只能上下左右移动,目标单元格值必须严格大于当前,要找出从左上角到右下角的所有有效路径。
方案1:递归回溯 + 缓存优化(直观易读)
递归回溯是最直接的思路——从起点出发,遍历所有合法的下一步,直到走到终点,记录所有有效路径。为了支持复用,我把逻辑封装成了无状态函数,还加了缓存避免重复计算。
这里有个关键优化:因为路径是严格递增的,所以绝对不会走回头路(回头的单元格值肯定比当前小),所以不需要额外维护visited集合,省了不少事!
from functools import lru_cache from typing import List def find_valid_paths(grid: List[List[int]]) -> List[List[int]]: # 处理空网格的边界情况 if not grid or not grid[0]: return [] rows, cols = len(grid), len(grid[0]) end_i, end_j = rows - 1, cols - 1 start_val, end_val = grid[0][0], grid[end_i][end_j] # 如果终点值不大于起点,直接返回空 if end_val <= start_val: return [] # 递归函数:返回从(i,j)到终点的所有路径 @lru_cache(maxsize=None) def dfs(i: int, j: int) -> List[List[int]]: current_val = grid[i][j] # 到达终点,返回仅包含终点的路径 if i == end_i and j == end_j: return [[current_val]] paths = [] # 遍历四个方向 for di, dj in [(-1, 0), (1, 0), (0, -1), (0, 1)]: ni, nj = i + di, j + dj # 检查是否在网格内,且目标值严格更大 if 0 <= ni < rows and 0 <= nj < cols and grid[ni][nj] > current_val: # 把当前值加到每条子路径的开头 for sub_path in dfs(ni, nj): paths.append([current_val] + sub_path) return paths # 从起点开始递归 return dfs(0, 0)
测试示例
grid = [[1,4,3], [5,6,7]] print(find_valid_paths(grid)) # 输出:[[1,4,6,7], [1,5,6,7]],完美匹配你的预期!
为什么支持可逆使用?
- 函数是无状态的,每次传入不同网格都会独立计算,不会互相干扰
lru_cache会缓存每个位置到终点的路径集合,如果同一网格被多次调用,第二次直接读缓存,速度更快- 输入输出清晰,随时可以调用
方案2:迭代式DFS(避免递归栈溢出)
如果你的网格特别大(比如1000x1000),递归可能会触发栈溢出。这时候迭代式DFS就更靠谱了,思路和递归一样,只是用栈来模拟递归过程。
from typing import List def find_valid_paths_iterative(grid: List[List[int]]) -> List[List[int]]: if not grid or not grid[0]: return [] rows, cols = len(grid), len(grid[0]) end_i, end_j = rows - 1, cols - 1 start_val, end_val = grid[0][0], grid[end_i][end_j] if end_val <= start_val: return [] # 栈元素:(当前行, 当前列, 当前路径) stack = [(0, 0, [start_val])] valid_paths = [] while stack: i, j, current_path = stack.pop() # 到达终点,加入结果列表 if i == end_i and j == end_j: valid_paths.append(current_path) continue current_val = grid[i][j] # 遍历四个方向(栈是后进先出,想和递归顺序一致的话可以调整方向顺序) for di, dj in [(-1, 0), (1, 0), (0, -1), (0, 1)]: ni, nj = i + di, j + dj if 0 <= ni < rows and 0 <= nj < cols and grid[ni][nj] > current_val: # 复制当前路径并添加新值,压入栈 new_path = current_path.copy() new_path.append(grid[ni][nj]) stack.append((ni, nj, new_path)) # 反转结果,让顺序和递归方案一致 return valid_paths[::-1]
测试同样的网格,输出结果和递归方案完全一致。
进阶:类封装+缓存(适合高频复用)
如果需要频繁处理同一网格,用类来封装缓存会更方便,避免每次调用都重新计算:
from functools import lru_cache from typing import List class PathFinder: def __init__(self): # 用网格的元组形式作为缓存键(列表不可哈希,转成元组) self._cache = {} def find_paths(self, grid: List[List[int]]) -> List[List[int]]: grid_tuple = tuple(tuple(row) for row in grid) # 先查缓存,存在直接返回 if grid_tuple in self._cache: return self._cache[grid_tuple] if not grid or not grid[0]: result = [] else: rows, cols = len(grid), len(grid[0]) end_i, end_j = rows - 1, cols - 1 if grid[end_i][end_j] <= grid[0][0]: result = [] else: @lru_cache(maxsize=None) def dfs(i, j): if i == end_i and j == end_j: return [[grid[i][j]]] paths = [] for di, dj in [(-1,0), (1,0), (0,-1), (0,1)]: ni, nj = i+di, j+dj if 0<=ni<rows and 0<=nj<cols and grid[ni][nj]>grid[i][j]: for sub_path in dfs(ni, nj): paths.append([grid[i][j]] + sub_path) return paths result = dfs(0, 0) # 存入缓存 self._cache[grid_tuple] = result return result
使用方式
# 初始化一个路径查找器 finder = PathFinder() # 第一次计算,存入缓存 grid1 = [[1,4,3], [5,6,7]] print(finder.find_paths(grid1)) # 第二次直接读缓存,速度更快 print(finder.find_paths(grid1)) # 处理新网格 grid2 = [[2,3], [1,4]] print(finder.find_paths(grid2))
注意事项
- 如果终点无法从起点到达(比如被更小的单元格完全阻断),函数会返回空列表
- 递归方案的深度受限于Python默认递归栈深度(默认1000),大网格优先选迭代方案
- 因为路径严格递增,所以不用担心循环路径的问题
内容的提问来源于stack exchange,提问作者ageow
相关产品推荐
相关产品推荐

