软通外包现在都要211、985了吗?
软通外包都开始盯着211、985了,这画面确实有点魔幻。以前大家吐槽外包是“活多钱紧背锅快”,现在门槛还往上拧,整得像大厂嫡系选秀。
网友有句话挺损:这是用校招标准,发派遣的工资。还有人说得更直,甲方要体面,成本又不想涨,最后就拿学历当筛子,先刷掉一批再说。
最逗的是,真进去了干的还是那些熟得发腻的活,文档、对接、改需求、背时点,一个都不会因为你是985就少一点。HR把JD一挂,估计自己都知道这要求写出来有点虚。
说白了,不是岗位突然高级了,是市场冷了,简历太多了,外包也开始挑了。最惨的还是普通本科,看着这行越来越像“高配打杂”。
面试题:生命游戏
一上来就二维数组原地改,十有八九会把自己绕进去。
board[i][j] 这一轮刚被你改成了 0,下一格在统计邻居时看到的就已经不是“上一轮状态”了。生命游戏这题麻烦不在规则,麻烦在一边读旧状态,一边又想写新状态,手一快就串味了。这个坑跟线上改缓存很像,读写混在一起,最后谁也说不清到底按哪份数据算的。写这类题,我第一反应不是怼循环,而是先把“旧”和“新”隔开。
这题规则就不铺了,核心只有两步:先数每个细胞周围 8 个方向有几个活细胞,再按规则决定它下一轮活还是死。
最稳的写法,是开一个临时数组 next。空间不是最优,但代码干净,排查也顺手。面试里先把正确性拿下来,比硬憋原地算法靠谱。
publicclassSolution{
privatestaticfinalint[] DX = {-1, -1, -1, 0, 0, 1, 1, 1};
privatestaticfinalint[] DY = {-1, 0, 1, -1, 1, -1, 0, 1};
publicvoidgameOfLife(int[][] board){
int m = board.length, n = board[0].length;
int[][] next = newint[m][n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
int live = countLive(board, i, j, m, n);
if (board[i][j] == 1) {
next[i][j] = (live == 2 || live == 3) ? 1 : 0;
} else {
next[i][j] = (live == 3) ? 1 : 0;
}
}
}
for (int i = 0; i < m; i++) {
System.arraycopy(next[i], 0, board[i], 0, n);
}
}
privateintcountLive(int[][] board, int x, int y, int m, int n){
int cnt = 0;
for (int k = 0; k < 8; k++) {
int nx = x + DX[k];
int ny = y + DY[k];
if (nx >= 0 && nx < m && ny >= 0 && ny < n && board[nx][ny] == 1) {
cnt++;
}
}
return cnt;
}
}
这段代码没什么花活,但好处很实际。
第一,方向数组把 8 个邻居统一掉了,不容易漏。很多人喜欢手写 8 个坐标判断,改着改着就少一个右下角,结果样例都不一定挂,自己还以为过了。
第二,规则判断别写得太拧巴。活细胞只在 2 或 3 时存活,死细胞只在 3 时复活,直接摊开写,后面自己回头看也快。
这题如果继续抠优化,可以做原地状态编码。比如 -1 表示“原来活,现在死”,2 表示“原来死,现在活”。统计邻居时,只认原始活状态,也就是 1 和 -1。这样能把额外空间压到 O(1),但代码可读性会差一点。真在面试里写,我一般是先把上面这个版本写稳,再补一句“可以用状态标记做原地优化”,思路分和实现分都不丢。
算法复杂度也很直接,遍历一遍矩阵,每个格子看 8 个方向,时间复杂度 O(m * n),额外空间 O(m * n)。
这题看着像模拟,实际考的是你有没有把“旧状态”和“新状态”分干净。很多题不是不会写,是一上来就想省空间,结果把自己省没了。