面试准备

最近改动

更新题目 螺旋遍历:遇障碍转向

题目 temu-spiral-blockers · 修改(rev 6) · cc · 2026-10-26 00:04

body

@@ -1,41 +1,96 @@
+## TL;DR
+
+- 先答 [[q:lc54]] 原题,follow-up 是矩阵里有障碍格,碰到就顺时针转向,只输出按规则走得到的格子(见「题目」)。
+- 四边界收缩的写法处理不了障碍,改用「方向数组 + visited」模拟,这个写法同时也能答原题(见「思路」)。
+- 每一步先试直走,走不通再按顺时针换方向。先试右转会把原题都走错(见「代码」里的对照)。
+- 右转后还走不通时,是继续转还是直接结束,开写前要问,两种理解在 `[[1, -1], [3, 4]]` 上结果不同(见「题目」里要问的问题)。
+- 同一轮也可能考生成版 [[q:lc59]],代码只差把「读值」换成「填数」(见「Follow-up」)。
+
 ## 题目
 
-先是 [[q:lc54]] 原题:m × n 矩阵,从外到内顺时针螺旋输出所有值。
-
-Follow-up:矩阵里有障碍格(blocker,有的帖子写成值为 -1 的格子)。走的时候碰到障碍就要转向,转向一律顺时针(右转),问最后的输出顺序。
-
-> [!warning] 一亩三分地 2026-10 那篇店面的楼主在回复里补充:**不一定要输出全部数据,只要输出按规则能走到的那些格子**——也就是说,被障碍挡住、永远走不到的格子不输出。其他细节(障碍格本身肯定不输出、四个方向都走不了就结束)是按最自然的理解【推断】的,面试时还是先和面试官对一遍。
+先是 [[q:lc54]] 原题:给一个 m × n 矩阵,从左上角出发,从外到内顺时针螺旋输出所有值。
+
+Follow-up:矩阵里有障碍格(blocker),有的帖子把它写成值为 -1 的格子。走的时候碰到障碍就要转向,转向一律顺时针(右转),问最后的输出顺序。
+
+> [!note] 一篇店面的楼主在回复里补充了题意:不一定要输出全部数据,只输出按规则能走到的那些格子。被障碍挡住、永远走不到的格子不输出。
+
+例子(障碍写成 -1):
+
+```
+1   2   3   4
+5  -1   7   8
+9  10  11  12
+```
+
+输出 `[1, 2, 3, 4, 8, 12, 11, 10, 9, 5]`。从 1 往右走到 4,碰到右边界转向下走到 12,碰到下边界转向左走到 9,碰到左边界转向上走到 5。在 5 这里,上面的 1 已经走过,右边是障碍,下面的 9 走过,左边越界,四个方向都走不了,结束。7 永远走不到,所以不输出。
+
+### 要和面试官对清楚的问题
+
+口头题面只说了「碰到障碍顺时针转」,下面几处是留白。第一条楼主确认过,其余是按最自然的理解【推断】的,面试时先问。
+
+| 问题 | 通常的答案 | 答案不同时代码怎么改 |
+| --- | --- | --- |
+| 走不到的格子输出吗 | 不输出(楼主确认) | 按走路规则永远到不了这些格子,要输出就得另定一条规则决定它们排在哪里(比如走完后按行扫一遍补上),先问清这条规则,再在 `return out` 之前补一段 |
+| 障碍格本身输出吗 | 不输出【推断】 | 不用改。障碍格本来就进不去 |
+| 什么时候结束 | 当前格四个方向都走不了就结束【推断】 | 不用改 |
+| 右转后还走不通怎么办 | 继续顺时针转,直到找到能走的方向【推断】 | 只允许右转一次的话,把内层循环的 `range(4)` 改成 `range(2)` |
+| 障碍怎么表示 | 值为 -1 | 代码里 `blocker` 是参数,换成别的值直接传 |
+
+第四个问题最容易被忽略。「继续顺时针转」意味着转三次,转三次等于左转。在下面这个 2 × 2 的例子里两种理解结果不同:
+
+```
+1  -1
+3   4
+```
+
+- 从 1 出发向右,右边是障碍,右转向下走到 3。在 3 这里方向是向下,下面越界,右转是向左,也越界。
+- 「继续转」的理解:再转一次是向上(1 走过),再转一次是向右,走到 4,输出 `[1, 3, 4]`。
+- 「只右转一次」的理解:右转后还走不通就结束,输出 `[1, 3]`。
+
+2026-10-26 用脚本枚举了 1 × 1 到 4 × 4、最多两个障碍的所有网格,两种理解结果不同的网格有 125 个,面积最小的只有这一个。没有障碍时两种理解永远一样,因为原题里从来不需要左转(2026-10-26 在 1 × 1 到 10 × 10 的全部无障碍矩阵上跑过,两种理解和四边界写法的输出完全相同)。
+
+> [!say] Before coding, I'd like to confirm two things. First, cells that the blockers make unreachable are simply not printed. Second, if turning right once still doesn't work, do I keep rotating, which effectively means turning left, or do I stop there?
 
 ## 面经里怎么考的
 
-**这是 Temu 第一轮技术面出现最多的题,Order 组尤其常见。** 2026-10-18 把一亩三分地上 Temu 最近两年的帖子读完后,螺旋矩阵([[q:lc54]])或者它的生成版([[q:lc59]])在第一轮出现了七次,其中三篇写明是 order / ordering 组:
-
-- 2026-10 两篇店面(发帖人不同,内容几乎一样):先是 LC 54 原题,follow-up 加障碍格(blocker),遇到障碍就顺时针右转。其中一篇楼主在回复里说清了题意:**不要求输出全部数据,只要输出按规则能走到的那些格子**——和下面「代码」那一节的理解一致,被挡住、走不到的格子就不输出。
-- 2026-07 一篇 temu 一面(过):LC 59 的 m × n 版本,follow-up 是遇到 -1 也要转向。回复里有人举了个例子问输出是什么,说明这个规则当场要和面试官对清楚。
-- 2026-07 一篇 ordering 组 Senior 第一轮(过):30 分钟深挖过往项目,然后 LC 54 和 [[q:lc98]] 各一道。
-- 2026-08 一篇 Orders 组 Java 岗第一轮:LC 59,**要求用 Java 写**,面试平台只能用 Chrome 打开。
-- 2026-06 一篇 order team coding screen(挂):给 m、n 生成 m × n 的螺旋矩阵(LC 59 的推广),楼主说忘了怎么写。
-- 2026-03 一篇第一轮:LC 59。
-
-教训:这道题在面试里通常只占后 20 分钟左右(前面半小时到 40 分钟是深挖简历),所以要能**一次写对、边写边讲**,并且原题、生成版、带障碍的 follow-up 三种都要熟。
+这道题出现在 Temu 第一轮技术面,order 组最常见。考法是先写 [[q:lc54]] 原题,写完后面试官加障碍格作为 follow-up,规则是遇到障碍就顺时针右转。有楼主在回复里说清了题意:只输出按规则能走到的格子。
+
+同一个组的第一轮也常考它的生成版 [[q:lc59]]:给 m 和 n,按螺旋顺序把 1, 2, 3, …… 填进 m × n 矩阵。有一篇面经的 follow-up 是填的时候遇到 -1 也要转向,回复里还有人举了一个例子问输出是什么,说明这条规则当场要和面试官对清楚。另一篇 order team 的 coding screen 考的就是生成版,楼主说忘了怎么写,挂了。
+
+其他几篇面经提供了这一轮的形式:
+
+- ordering 组一篇 Senior 第一轮先花 30 分钟深挖过往项目,然后做 LC 54 和 [[q:lc98]] 各一道。
+- Orders 组一篇 Java 岗第一轮考的是 LC 59,要求用 Java 写,面试平台只能用 Chrome 打开。
+
+这道题在面试里通常只占后 20 分钟左右,前面半小时到 40 分钟是深挖简历。所以要能一次写对、边写边讲,原题、生成版、带障碍的 follow-up 三种都要熟。
 
 ## 思路
 
-原题常见写法是四条边界收缩;但有了障碍格,边界法就不好用了。改成**方向数组 + visited 模拟**:
-
-- 方向按右、下、左、上排;当前方向下一格越界、走过、或者是障碍,就顺时针换下一个方向。
-- 四个方向都走不了就结束。
-- 不输出障碍格。
-
-这个写法对没有障碍的矩阵也正确,所以原题直接用它答,follow-up 只多一个判断,面试时很顺。
+原题常见写法是四条边界收缩:维护上、下、左、右四条边界,每走完一圈把四条边界各往里收一格。这个写法把路线写死成一圈一圈的矩形,障碍格没法让它中途转向。最多只能做到「碰到障碍格跳过不输出」,结果和题意不符。在题目的例子上,它会多输出走不到的 7(输出见「代码」里的对照)。
+
+所以改成直接模拟一个人在格子里走:
+
+- 记住当前位置 `(r, c)` 和当前方向 `d`。方向按右、下、左、上排成数组 `dirs`,下标加 1 就是顺时针转 90 度。
+- 用 visited 数组 `seen` 记下走过的格子。
+- 每一步从当前方向开始,依次试 `d`、`d+1`、`d+2`、`d+3`(对 4 取模)四个方向。代码里用 `t` 表示偏移量:`t = 0` 是直走,`t = 1` 是右转,`t = 2` 是掉头,`t = 3` 是左转(顺时针转三次)。
+- 某个方向的下一格没越界、没走过、也不是障碍,就走过去,并把方向更新成这个方向。四个都不行就结束。
+
+`t = 2`(掉头)永远走不通,原因是除了起点,来的那一格一定走过,而起点向左是越界。保留它只是为了让「依次试四个方向」的循环写起来整齐。
+
+在没有障碍的前提下,这个写法输出的就是原题的螺旋顺序。原因是没有障碍时,触发转向的只有越界和走过两种情况。沿外圈走到边界会右转,走完一圈回到起点下方时,上面那格走过,于是右转进入内圈。外圈走过之后,走过的格子就相当于往里收了一格的边界,内圈照同样的规则再走一圈,这和原题的路线一致。所以面试时原题直接用它答,follow-up 只在判断条件里多加 `grid[nr][nc] != blocker`。
+
+> [!say] Instead of shrinking four boundaries, I simulate the walk. I keep a direction index and a visited grid. At each step I first try to go straight, and whenever the next cell is out of bounds, visited, or a blocker, I rotate clockwise and try again. If all four directions fail, I stop.
 
 ## 代码
 
 ```python
-def spiral_with_blockers(grid, blocker="#"):
-    m, n = len(grid), len(grid[0]) if grid else 0
-    if not m or not n or grid[0][0] == blocker:
+DIRS = [(0, 1), (1, 0), (0, -1), (-1, 0)]   # 右、下、左、上:顺时针
+
+
+def spiral_with_blockers(grid, blocker=-1):
+    m = len(grid)
+    n = len(grid[0]) if m else 0
+    if m == 0 or n == 0 or grid[0][0] == blocker:
         return []
-    dirs = [(0, 1), (1, 0), (0, -1), (-1, 0)]   # 右、下、左、上:顺时针
     seen = [[False] * n for _ in range(m)]
     r = c = d = 0
@@ -43,7 +98,7 @@
     out = [grid[0][0]]
     while True:
-        for t in range(4):
+        for t in range(4):              # t = 0 直走,t = 1 右转,t = 2 掉头,t = 3 左转
             nd = (d + t) % 4
-            nr, nc = r + dirs[nd][0], c + dirs[nd][1]
+            nr, nc = r + DIRS[nd][0], c + DIRS[nd][1]
             if 0 <= nr < m and 0 <= nc < n and not seen[nr][nc] and grid[nr][nc] != blocker:
                 d, r, c = nd, nr, nc
@@ -51,26 +106,208 @@
                 out.append(grid[r][c])
                 break
+        else:                           # 四个方向都没 break,走不动了
+            return out
+```
+
+`for ... else` 的 `else` 只在循环没有被 `break` 打断时执行,这里就是「四个方向都试过、都走不通」。
+
+### 正确版本和三个错误版本的对照
+
+三个错误版本都是面试里容易写出来的:
+
+- `wrong_turn_first`:每一步先试右转,直走排到最后。
+- `wrong_no_visited`:忘了 visited,只判断越界和障碍。
+- `wrong_boundary_skip`:沿用原题的四边界收缩,遇到障碍格跳过不输出。
+
+前两个和正确版本只差一行,其余照抄 `spiral_with_blockers`:
+
+```python
+# wrong_turn_first:内层循环改成先试右转
+        for t in (1, 2, 3, 0):
+
+# wrong_no_visited:判断条件去掉 not seen[nr][nc]
+            if 0 <= nr < m and 0 <= nc < n and grid[nr][nc] != blocker:
+```
+
+`wrong_no_visited` 停不下来,测试时在 `while True:` 下面第一行加了 `if len(out) >= 30: return out + ["..."]`,输出满 30 个就截断。
+
+`wrong_boundary_skip` 是原题的标准写法,只在最后把障碍值滤掉:
+
+```python
+def wrong_boundary_skip(grid, blocker=-1):
+    if not grid or not grid[0]:
+        return []
+    top, bot, left, right = 0, len(grid) - 1, 0, len(grid[0]) - 1
+    out = []
+    while top <= bot and left <= right:
+        for c in range(left, right + 1):            # 上边,向右
+            out.append(grid[top][c])
+        for r in range(top + 1, bot + 1):           # 右边,向下
+            out.append(grid[r][right])
+        if top < bot:
+            for c in range(right - 1, left - 1, -1):    # 下边,向左
+                out.append(grid[bot][c])
+        if left < right:
+            for r in range(bot - 1, top, -1):       # 左边,向上
+                out.append(grid[r][left])
+        top, bot, left, right = top + 1, bot - 1, left + 1, right - 1
+    return [x for x in out if x != blocker]
+```
+
+`if top < bot` 和 `if left < right` 防的是只剩一行或一列时,下边和左边把上边、右边已经输出的格子再输出一遍。
+
+已运行(2026-10-26,Python 3.13.17),四个函数在同一组输入上的输出:
+
+| 输入 | 正确版本 | `wrong_turn_first` | `wrong_boundary_skip` | `wrong_no_visited` |
+| --- | --- | --- | --- | --- |
+| `[[1,2,3],[4,5,6],[7,8,9]]` | `[1,2,3,6,9,8,7,4,5]` | `[1,4,5,8,7]` | `[1,2,3,6,9,8,7,4,5]` | `1,2,3,6,9,8,7,4` 反复循环 |
+| `[[1,2,3,4],[5,-1,7,8],[9,10,11,12]]` | `[1,2,3,4,8,12,11,10,9,5]` | `[1,5,9,10,11,7,8,12]` | `[1,2,3,4,8,12,11,10,9,5,7]` | `1,2,3,4,8,12,11,10,9,5` 反复循环 |
+| `[[1,2,-1],[4,5,6],[7,8,9]]` | `[1,2,5,8,7,4]` | `[1,4,5,8,7]` | `[1,2,6,9,8,7,4,5]` | `1,2,5,8,7,4` 反复循环 |
+
+逐个看错在哪:
+
+- `wrong_turn_first` 在没有障碍的 3 × 3 上就错了。从 1 出发,先试右转(向下),走到 4,再先试右转(向左)越界,掉头(向上)是走过的 1,于是左转(向右)走到 5,之后一路偏离螺旋。
+- `wrong_boundary_skip` 在没有障碍时是对的,所以只测原题看不出问题。第二个输入里它多输出了走不到的 7。第三个输入里它没有在障碍处转向,按外圈顺序输出了 6、9,从第三个数起就和正确版本不同。
+- `wrong_no_visited` 走完外圈回到 1 时,1 不是障碍、也没越界,于是继续绕外圈,永远停不下来。
+
+## 复杂度
+
+时间 O(mn)。每个格子进去后马上标记 visited,之后不会再进,所以外层循环最多走 mn 步,每步最多试 4 个方向。
+
+空间 O(mn),用在 `seen` 上(不算输出)。可以把走过的格子原地改成障碍值来省掉 `seen`,但会改坏输入,要先问(见「Follow-up」)。
+
+## 易错点
+
+- 每一步先试直走(`t = 0`),走不通再转。先试右转连原题都会走错(见「代码」里的对照)。
+- 忘了 visited 会绕着外圈死循环,visited 也是「转进内圈」的触发条件(见「思路」)。
+- 矩阵为空、起点就是障碍,要在开头先返回空列表。
+- 行列不等时边界分开写 `0 <= nr < m and 0 <= nc < n`,见 [[p:me/python-pitfalls]]。
+- 只测原题看不出四边界写法处理不了障碍,测试时一定带一个会挡住格子的障碍。
+
+## 测试用例
+
+已运行(2026-10-26),除了上面对照表里的三组:
+
+| 输入 | 输出 | 测的是什么 |
+| --- | --- | --- |
+| `[[1,2,3,4],[5,6,7,8],[9,10,11,12]]` | `[1,2,3,4,8,12,11,10,9,5,6,7]` | 行列不等、无障碍 |
+| `[[1,-1],[3,4]]` | `[1,3,4]` | 需要左转的情况,「只右转一次」的理解下是 `[1,3]` |
+| `[[-1]]` | `[]` | 起点就是障碍 |
+| `[]` | `[]` | 空矩阵 |
+| `[[7]]` | `[7]` | 单个格子 |
+| `[[1,2,3]]` | `[1,2,3]` | 单行 |
+| `[[1],[2],[3]]` | `[1,2,3]` | 单列 |
+
+对照表的第二行(7 走不到)和第三行(3、6、9 走不到)正好拿来和面试官确认规则。
+
+## Follow-up
+
+### 生成版:按螺旋顺序填数,遇到 -1 转向
+
+这是 [[q:lc59]] 的 m × n 推广,再加上障碍。做法和上面一样,只是走到一格时填一个递增的数,不读值。矩阵初始全是 0,障碍格是 -1,所以「格子值为 0」同时表示没走过、也不是障碍,不需要单独的 visited。
+
+它是对的,原因和遍历版一样:每格第一次走到就被填成正数,之后值不再是 0,不会被第二次走进,转向规则也完全相同,所以填数的顺序就是遍历版的输出顺序。代价和遍历版相同,时间 O(mn),除了要返回的矩阵本身不需要额外空间。
+
+面经只说「遇到 -1 也要转向」,没写障碍怎么给出。下面的代码让调用方传障碍坐标列表 `blockers`,这是【推断】的输入格式。面试官也可能直接给一个已经放好 -1 的矩阵,那就把它当 `g` 用,省掉前三行。走不到的格子留 0【推断】,填 0 还是别的值要先问面试官。
+
+```python
+DIRS = [(0, 1), (1, 0), (0, -1), (-1, 0)]
+
+
+def spiral_fill(m, n, blockers=()):
+    g = [[0] * n for _ in range(m)]
+    for r, c in blockers:
+        g[r][c] = -1
+    if m == 0 or n == 0 or g[0][0] == -1:
+        return g
+    r = c = d = 0
+    k = 1
+    g[0][0] = k
+    while True:
+        for t in range(4):
+            nd = (d + t) % 4
+            nr, nc = r + DIRS[nd][0], c + DIRS[nd][1]
+            if 0 <= nr < m and 0 <= nc < n and g[nr][nc] == 0:   # 0 = 没走过也不是障碍
+                d, r, c = nd, nr, nc
+                k += 1
+                g[r][c] = k
+                break
+        else:
+            return g
+```
+
+已运行(2026-10-26):
+
+- `spiral_fill(3, 3)` → `[[1,2,3],[8,9,4],[7,6,5]]`,和 LC 59 一致。
+- `spiral_fill(3, 4)` → `[[1,2,3,4],[10,11,12,5],[9,8,7,6]]`。
+- `spiral_fill(3, 4, [(1, 1)])` → `[[1,2,3,4],[10,-1,0,5],[9,8,7,6]]`,走到 10 时右边是障碍,结束,`(1, 2)` 留 0。
+- `spiral_fill(3, 3, [(0, 2)])` → `[[1,2,-1],[6,3,0],[5,4,0]]`。
+
+### 不用 visited 数组,原地标记
+
+读完一格就把它改成障碍值,这样「走过」和「障碍」合成同一个判断,省掉 O(mn) 的 `seen`。代价是输入被改坏,调用方之后再读这个矩阵会出错,所以要先问面试官能不能改输入。
+
+```python
+def spiral_in_place(grid, blocker=-1):
+    m = len(grid)
+    n = len(grid[0]) if m else 0
+    if m == 0 or n == 0 or grid[0][0] == blocker:
+        return []
+    r = c = d = 0
+    out = [grid[0][0]]
+    grid[0][0] = blocker
+    while True:
+        for t in range(4):
+            nd = (d + t) % 4
+            nr, nc = r + DIRS[nd][0], c + DIRS[nd][1]
+            if 0 <= nr < m and 0 <= nc < n and grid[nr][nc] != blocker:
+                d, r, c = nd, nr, nc
+                out.append(grid[r][c])
+                grid[r][c] = blocker
+                break
         else:
             return out
 ```
 
-已运行(2026-10-17):
-
-- 无障碍 3 × 3 → `[1, 2, 3, 6, 9, 8, 7, 4, 5]`,和原题一致。
-- `[[1,2,3,4],[5,#,7,8],[9,10,11,12]]` → `[1, 2, 3, 4, 8, 12, 11, 10, 9, 5]`:走到 5 时右边是障碍、上面走过,就结束了,**7 永远走不到**。
-- `[[1,2,#],[4,5,6],[7,8,9]]` → `[1, 2, 5, 8, 7, 4]`:3、6、9 走不到。
-
-后两个例子正好用来和面试官确认规则。
-
-## 复杂度
-
-O(mn) 时间,每格最多进一次、每次最多试 4 个方向;O(mn) 空间存 visited(可以原地把走过的格子改成标记来省空间,但会改输入,先问)。
-
-## 易错点
-
-- 「转向」时要从当前方向开始试(`t = 0` 是继续直走),而不是每步都先右转。
-- 起点就是障碍、矩阵为空,要先处理。
-
-## 英文讲法
-
-> [!say] Instead of shrinking four boundaries, I simulate: keep a direction index and a visited grid, and whenever the next cell is out of bounds, visited, or a blocker, rotate clockwise. If all four directions fail, I stop. Before coding, I'd confirm whether blocked cells can leave some cells unreachable, because with this rule they can.
+已运行(2026-10-26):输入 `[[1,2,3,4],[5,-1,7,8],[9,10,11,12]]`,输出 `[1,2,3,4,8,12,11,10,9,5]`,和正确版本一样。跑完后矩阵变成 `[[-1,-1,-1,-1],[-1,-1,7,-1],[-1,-1,-1,-1]]`,只剩走不到的 7 没被改。
+
+### 要求用 Java 写
+
+有一篇面经要求用 Java。用 Python 面试可以跳过这一条。Java 写法和思路、复杂度都和 Python 版本相同,它特有的坑和完整代码放在下面的可选知识框里。
+
+> [!optional] Java 没有 `for ... else`,要换成一个标志 `moved`:每一步开始时设成 `false`,走成功了设成 `true` 再 `break`,内层循环结束后 `moved` 还是 `false` 就返回。`moved` 必须在每一步开头重置。放到 `while` 外面的话,第一步走成功后它一直是 `true`,走不动时也不会返回,会死循环。已运行(2026-10-26,OpenJDK 21.0.12.1):把 `moved` 挪到 `while` 外面后,第一个输入就卡住,3 秒超时被终止,什么都没打印。
+>
+> 下面的正确版本也已运行(同一天)。四个输入依次是无障碍 3 × 3、题目的例子、`[[1,2,-1],[4,5,6],[7,8,9]]`、`[[1,-1],[3,4]]`,输出 `[1, 2, 3, 6, 9, 8, 7, 4, 5]`、`[1, 2, 3, 4, 8, 12, 11, 10, 9, 5]`、`[1, 2, 5, 8, 7, 4]`、`[1, 3, 4]`,和 Python 版本一致。
+>
+> ```java
+> import java.util.*;
+>
+> public class Spiral {
+>     static final int[][] DIRS = {{0, 1}, {1, 0}, {0, -1}, {-1, 0}};
+>
+>     static List<Integer> spiralWithBlockers(int[][] grid, int blocker) {
+>         List<Integer> out = new ArrayList<>();
+>         int m = grid.length, n = m == 0 ? 0 : grid[0].length;
+>         if (m == 0 || n == 0 || grid[0][0] == blocker) return out;
+>         boolean[][] seen = new boolean[m][n];
+>         int r = 0, c = 0, d = 0;
+>         seen[0][0] = true;
+>         out.add(grid[0][0]);
+>         while (true) {
+>             boolean moved = false;
+>             for (int t = 0; t < 4; t++) {
+>                 int nd = (d + t) % 4;
+>                 int nr = r + DIRS[nd][0], nc = c + DIRS[nd][1];
+>                 if (nr >= 0 && nr < m && nc >= 0 && nc < n && !seen[nr][nc] && grid[nr][nc] != blocker) {
+>                     d = nd; r = nr; c = nc;
+>                     seen[r][c] = true;
+>                     out.add(grid[r][c]);
+>                     moved = true;
+>                     break;
+>                 }
+>             }
+>             if (!moved) return out;
+>         }
+>     }
+> }
+> ```

meta

@@ -1 +1,5 @@
-{}
+{
+ "depth_guide": "2026-10-23",
+ "rewritten_by": "cloud:20261025-2345",
+ "rewritten_at": "2026-10-26"
+}