程序员老鬼

朋友说他们组有个30岁、年薪85万的高级工程师,被调离了核心项目。原因不是技术不行,而是拒绝参与流程管理和协作优化。

刚看到个贴子,说网友吐槽:朋友所在团队有个30岁、年薪85万的高级工程师,被从核心项目拿下,不是技术不行,而是死活不愿意做流程管理、协作优化。Leader还来句:“他是能跑的马,但不愿意拉车。”

Image

我觉得这事吧,关键在“岗位匹配”。你拿的是85万的价,不只是代码能力的价,而是“带动团队、让系统运转更顺”的价。核心项目不是个人秀,更像接力赛,你再能冲,如果不愿交接、不愿配合,Leader肯定更担心整体节奏。

当然,公司一边也要想明白:到底要专家型还是管理型,别啥活都往人身上堆。

但说到底,成人的职场选择是:要么接受角色的全套要求,要么就选一个更适合自己的位置。想清楚,比什么都重要。

面试题:单词搜索

昨天晚上十一点多吧,我在工位旁边那台咖啡机前面愣神儿,困得眼皮打架,我们组小李突然凑过来问我一句:“哥,那个单词搜索你咋写得又快又稳啊,我一写就超时还老写错。”我说你别急,这题看着像玩拼字游戏,其实跟线上排查那种“链路一层层往下钻”一个味儿,你一旦把路径这事想明白了,就不容易翻车。

你想啊,给你一个二维格子,里面一堆字母,你要在里面找一个 word,能上下左右走,不能斜着,格子还不能重复用。你把它当成我们线上请求走链路:从某个入口点进去,往四个方向试探,能走就继续,走不通就回滚,回滚这俩字很关键哈,跟数据库事务回滚差不多意思,就是“我刚刚试过了不行,我把现场恢复成原样,别影响后面其他分支”。这就是回溯,听着高大上,其实就是“试—不行—撤回—换路”。

我以前也犯过那种低级错,visited 用个 boolean[][],然后递归里忘了恢复,结果一条路走完把别的路都给污染了,跟你线上写了个静态变量没清干净一样,后面全乱套。后来我就干脆用一个更“土但好用”的办法:直接把 board[x][y] 临时改成一个不可能匹配的字符,比如 '#’,递归回来再改回去。嗯,这种写法就像你在排查问题时先打个临时日志标记,处理完再撤掉,不会影响主流程。

还有个小细节,别上来就 DFS 满地图跑,先做个最便宜的剪枝:当前格子字符要是跟 word 的当前位不一样,立马返回。你把它当成网关层的参数校验,能省一堆后端压力。然后边界也得稳:越界、已经用过、或者匹配完了,这几个出口要写得很清楚,不然你会在递归里迷路,真的,凌晨两点看递归栈比看抓包还想吐。

代码我给你们贴一份 Java 的,写得偏工程化一点点,变量名别太文艺哈,值班的时候你会感谢我:

publicclassWordSearch{

publicbooleanexist(char[][] board, String word){
if (board == null || board.length == 0 || board[0].length == 0) returnfalse;
if (word == null || word.isEmpty()) returntrue;

int m = board.length, n = board[0].length;
char[] target = word.toCharArray();

for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (dfs(board, i, j, target, 0)) returntrue;
            }
        }
returnfalse;
    }

privatebooleandfs(char[][] board, int x, int y, char[] target, int idx){
// idx 指向 target 当前要匹配的位置
if (idx == target.length) returntrue; // 全部匹配完了

// 越界
if (x < 0 || x >= board.length || y < 0 || y >= board[0].length) returnfalse;

// 字符不匹配 或 已访问(我用 '#' 当访问标记)
if (board[x][y] != target[idx]) returnfalse;

// 做标记
char backup = board[x][y];
        board[x][y] = '#';

// 四个方向继续试
boolean ok = dfs(board, x + 1, y, target, idx + 1)
                || dfs(board, x - 1, y, target, idx + 1)
                || dfs(board, x, y + 1, target, idx + 1)
                || dfs(board, x, y - 1, target, idx + 1);

// 现场恢复
        board[x][y] = backup;
return ok;
    }

// 随手写个小测试,别嫌弃哈,能跑就行
publicstaticvoidmain(String[] args){
        WordSearch ws = new WordSearch();
char[][] board = {
                {'A','B','C','E'},
                {'S','F','C','S'},
                {'A','D','E','E'}
        };

        System.out.println(ws.exist(board, "ABCCED")); // true
        System.out.println(ws.exist(board, "SEE"));    // true
        System.out.println(ws.exist(board, "ABCB"));   // false
    }
}

你看这段 dfs,核心就俩动作:进来先验证“能不能走”,能走就打标记,然后往四个方向递归,最后不管成不成功都把格子恢复。恢复这一下,很多人写漏,写漏了你会出现那种“明明答案在,怎么就是搜不到”的灵异事件,跟线上缓存没失效一模一样,表面看逻辑没错,实际状态脏了。

哦对了,还有人会问复杂度,我一般不在群里跟人抠公式,反正直觉就是:最坏情况每个点都可能扩展四个方向,长度是 L,差不多 O(mn4^L) 那种爆炸,所以剪枝和快速失败很重要。你要是再想抠一点性能,就可以先找首字母,或者在外层判断一下 board 里字符频次够不够,但别整太花,题目主要考你回溯写得稳不稳。