![[Python] 回溯中的 pop() 与 remove():以迷宫寻路为例](http://pic.xiahunao.cn/yaotu/[Python] 回溯中的 pop() 与 remove():以迷宫寻路为例)
引言在刷 LeetCode 或准备华为 OD 机试时DFS 回溯是高频考点。很多初学者在写回溯代码时会对path.pop()和visited.remove((x, y))产生疑惑它们不都是“弹出来”吗有什么区别为什么一个用pop一个用remove本文将以“迷宫寻路从左上到右下只能向右向下”这道题为例详细拆解这两个操作的异同帮助你彻底搞懂。问题背景给定一个m x n的网格0表示可通行1表示障碍物。从左上角(0,0)出发每次只能向右或向下移动一格要求找出一条能到达右下角(m-1, n-1)的路径任意一条即可。DFS 回溯的典型代码如下def find_path(grid): m, n len(grid), len(grid[0]) path [] # 记录路径 visited set() # 记录已访问的格子 def dfs(x, y): if x m-1 and y n-1: path.append((x, y)) return True visited.add((x, y)) path.append((x, y)) # 尝试向右和向下 for dx, dy in [(0, 1), (1, 0)]: nx, ny x dx, y dy if 0 nx m and 0 ny n and grid[nx][ny] 0 and (nx, ny) not in visited: if dfs(nx, ny): return True # 回溯撤销选择 path.pop() visited.remove((x, y)) return False if dfs(0, 0): return path else: return []在这段代码中path.pop()和visited.remove((x, y))分别扮演了什么角色为什么不能互换数据结构决定操作方式1.path是列表List列表是有序的、可重复的数据结构。我们用path记录从起点到当前格子的顺序路径。追加path.append((x, y))— 走到一个新格子时将其加入路径末尾。回溯path.pop()— 当当前路径走不通时撤销上一步的选择回到上一个格子。pop()移除并返回列表的最后一个元素正好符合“撤销最近一步”的需求。2.visited是集合Set集合是无序的、不重复的数据结构。我们用visited记录哪些格子已经被访问过防止重复探索。添加visited.add((x, y))— 进入一个新格子时将其加入已访问集合。回溯visited.remove((x, y))— 当当前路径走不通时需要将该格子从已访问集合中移除以便其他路径可以再次经过它。集合没有“最后一个”的概念所以我们只能根据值来删除即remove((x, y))。对比表格操作数据结构删除依据返回值适用场景list.pop()列表位置默认末尾被删除的元素撤销最近一步选择路径回溯set.remove(x)集合值无释放特定元素的占用标记回溯为什么不能互换假设我们尝试用visited.pop()来代替visited.remove((x, y))set.pop()会随机移除并返回一个元素而不是移除我们指定的(x, y)。这会导致已访问集合的状态混乱后续的路径探索可能会错误地认为某些格子尚未访问从而陷入死循环或产生错误路径。同样如果用path.remove((x, y))代替path.pop()list.remove(x)会移除列表中第一个值为x的元素。但如果路径中有重复的坐标理论上不会但万一有 bugremove可能删错位置。更重要的是remove需要遍历列表查找元素时间复杂度 O(n)而pop()是 O(1)。在深层递归中性能差距明显。总结pop()和remove()都是“移除”操作但服务于不同的数据结构。list.pop()有序结构的“撤销上一步”适合路径回溯。set.remove(x)无序结构的“释放指定元素”适合标记回溯。在 DFS 回溯中两者常常成对出现共同维护搜索状态的正确性。理解了这个区别你再写回溯代码时就不会混淆了。希望这篇文章对你有所帮助