程序员老鬼

完蛋! 第一次做外包好像对组长有点喜欢了

完蛋,外包第一课还没学会,先把组长看顺眼了,这剧情比工位恋爱还悬。人家是正编,你是外包,平时一起开会、对需求、改bug,气氛一上来,真容易脑子发热。

Image

可互联网这地方最会给人降温,传言张口就是“生殖隔离”“阶级区别”,听着像段子,扎心倒挺真。

评论区也挺损。有人说,喜欢可以,别把门禁权限当爱情通行证。还有人更直接,说组长今天夸你一句推进快,明天可能也这么夸三个人,别自己先写番外。

我看这事最难受的,不是喜欢谁,是你明知道关系里有层看不见的线。正编和外包,平时一起搬砖,真到利益、转正、归属感这些事上,味儿一下就变了。你以为自己是在心动,搞不好只是第一次被人当正常同事对待,有点上头。

面试题:墙与门

墙和门这题,表面看像个二维数组遍历,真写起来最容易犯的错,是拿到一个空房间就去找最近的门。这样也能做,但味道不对。

因为你会反复走很多冤枉路。

题目里有三种值:

  • -1 是墙,不能走
  • 0 是门
  • 2147483647 表示空房间,要求填成“到最近门的距离”

这题我第一眼就不太信 DFS。DFS 擅长一路钻到底,但这题要的是“最近”,天然更像按层扩散。哪个门先扩到这个房间,这个距离就是最短距离,不用再回头改。这个思路其实就是 多源 BFS:不是从一个起点出发,而是把所有门一起丢进队列,同时往外一圈一圈扩。这样的写法更稳,也更省。这个处理问题的手感,和线上查超时有点像:先抓最确定的入口,再一层层缩,不要一上来就乱钻。参考你给的技术文风格,我这里也按“现象—判断—代码”来落,不空讲概念。

先看核心代码,代码不用整太满,够说明问题就行:

import java.util.ArrayDeque;
import java.util.Queue;

publicclassSolution{
privatestaticfinalint INF = Integer.MAX_VALUE;
privatestaticfinalint[][] DIRS = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};

publicvoidwallsAndGates(int[][] rooms){
if (rooms == null || rooms.length == 0 || rooms[0].length == 0) {
return;
        }

int m = rooms.length, n = rooms[0].length;
        Queue<int[]> queue = new ArrayDeque<>();

// 先把所有门入队
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (rooms[i][j] == 0) {
                    queue.offer(newint[]{i, j});
                }
            }
        }

while (!queue.isEmpty()) {
int[] cur = queue.poll();
int x = cur[0], y = cur[1];

for (int[] d : DIRS) {
int nx = x + d[0];
int ny = y + d[1];

if (nx < 0 || nx >= m || ny < 0 || ny >= n) {
continue;
                }
// 只扩散到还没处理过的空房间
if (rooms[nx][ny] != INF) {
continue;
                }

                rooms[nx][ny] = rooms[x][y] + 1;
                queue.offer(newint[]{nx, ny});
            }
        }
    }
}

这段代码里真正关键的,不是 BFS 这三个字,而是两个判断:

第一,所有门同时入队。 这一下就把“最近门”这个事处理干净了,不需要每个房间单独算一次。

第二,只更新 INF 房间。 这表示一个房间只会被第一次到达时赋值。因为 BFS 是按距离一层层扩出去的,所以第一次到它,肯定就是最短距离。后面再绕远路过来,直接跳过。

举个很小的例子:

[
  [INF, -1, 0, INF],
  [INF, INF, INF, -1],
  [INF, -1, INF, -1],
  [0, -1, INF, INF]
]

跑完之后会变成:

[
  [3, -1, 0, 1],
  [2, 2, 1, -1],
  [1, -1, 2, -1],
  [0, -1, 3, 4]
]

这里如果你用“每个空房间找最近门”的思路,时间复杂度很容易炸到 O((mn)^2)。而多源 BFS 只会把每个格子进队一次,复杂度是 O(m*n),这才是这题该有的解法。

这题不难,难的是别把方向走反。凡是这种“多个起点、求最近距离、无权图最短路”的题,八成先想 BFS,别急着递归。递归写起来顺手,交上去不一定顺。停在这就够了。