刚刷到一个吐槽,说有个技术很菜的同事跳槽去了体制内。帖子的观点挺损,但细想又很扎心:像他这样有学历但技术短板明显的人,进体制内反而最能扬长避短,一点儿也不浪费个人优势。

这种人进了体制内,真就像切换了游戏地图。学历是硬通货,沟通表达也不算差,流程意识也具备。写材料、做对接、组织开会、向上汇报,这些活儿反倒能把他的优势发挥出来。技术短板没人天天拿放大镜盯着看,压力也不再是半夜 On-Call 或者周末紧急上线的玩法。
所以说,有时候真不是个人能力行不行的问题,而是赛道有没有选对。大厂需要的是能落地、能救火、能硬扛事儿的人;而体制内很多岗位要的则是稳重、守规矩、履历体面。
他可能不是“废了”,只是终于从地狱难度切换到了自己最舒服的模式。估计 HR 看完这份吐槽,都得沉默两秒。
面试题:四叉树交集
这道题最容易写错的地方,不在于递归,而在于题目名字本身。
“四叉树交集”这几个字一看,很多人的手速会比脑子快,直接写成 AND 逻辑。但这道题实际的合并逻辑,是求两个黑白矩阵的逻辑 OR 。也就是说,在同一块区域里,只要有一边是 true ,结果就应该是 true 。
四叉树的节点定义一般长这样:
class Node {
public boolean val;
public boolean isLeaf;
public Node topLeft;
public Node topRight;
public Node bottomLeft;
public Node bottomRight;
public Node() {}
public Node(boolean val, boolean isLeaf) {
this.val = val;
this.isLeaf = isLeaf;
}
public Node(boolean val, boolean isLeaf,
Node topLeft, Node topRight,
Node bottomLeft, Node bottomRight) {
this.val = val;
this.isLeaf = isLeaf;
this.topLeft = topLeft;
this.topRight = topRight;
this.bottomLeft = bottomLeft;
this.bottomRight = bottomRight;
}
}
拿到这题,我一般不急着去拆四个方向,而是先关注叶子节点。
因为叶子节点代表的是一整块完整的矩形区域,而不仅仅是一个网格。如果这个地方的判断顺序搞错了,代码就会多出许多毫无意义的递归调用。
比如,左边是一棵 true 的叶子节点:
node1: isLeaf = true, val = true
node2: 随便是什么
那合并后的结果,一定是一棵 true 的叶子节点。
这片区域已经全是 1 了,再和谁做 OR 运算结果都还是 1。后面的结构不用看,也不该去看。
反之,如果某一边是 false 叶子,那它对 OR 运算没有任何贡献,直接返回另一棵树的结构就行。
思路清晰后,代码可以这样组织:
class Solution {
public Node intersect(Node a, Node b) {
if (a == null) return b;
if (b == null) return a;
if (a.isLeaf) {
return a.val ? new Node(true, true) : cloneTree(b);
}
if (b.isLeaf) {
return b.val ? new Node(true, true) : cloneTree(a);
}
Node leftTop = intersect(a.topLeft, b.topLeft);
Node rightTop = intersect(a.topRight, b.topRight);
Node leftBottom = intersect(a.bottomLeft, b.bottomLeft);
Node rightBottom = intersect(a.bottomRight, b.bottomRight);
if (canMerge(leftTop, rightTop, leftBottom, rightBottom)) {
return new Node(leftTop.val, true);
}
return new Node(false, false, leftTop, rightTop, leftBottom, rightBottom);
}
private boolean canMerge(Node a, Node b, Node c, Node d) {
return a.isLeaf && b.isLeaf && c.isLeaf && d.isLeaf
&& a.val == b.val
&& b.val == c.val
&& c.val == d.val;
}
private Node cloneTree(Node node) {
if (node == null) return null;
if (node.isLeaf) {
return new Node(node.val, true);
}
return new Node(
node.val,
false,
cloneTree(node.topLeft),
cloneTree(node.topRight),
cloneTree(node.bottomLeft),
cloneTree(node.bottomRight)
);
}
}
这段代码里我加了个 cloneTree 辅助方法。这不是必须的,但我个人编码时更倾向于这么做。
有些算法题解会直接返回原节点的引用,提交到 LeetCode 上当然也能通过。但如果后续还有其他逻辑需要修改这棵树,共用了原树引用就很容易出现脏数据。在纯粹的算法题里可能无所谓,但在工程代码里,我一般不会偷这个懒。
这题真正的流程就四步,顺序千万不能乱:
- 先看
true 叶子:直接吞掉另一棵树,游戏结束。
- 再看
false 叶子:忽略它,直接把后续处理交给另一棵树。
- 两边都不是叶子:递归处理四个子区域。
- 递归回来后,检查四个子节点能否压缩成一个叶子节点。
最后这个压缩步骤非常关键。
试想一下,如果四个子区域最终全是 true,但你不做合并,结果虽然在逻辑上正确,可树的结构却不够简洁。四叉树的核心意义,就在于将连续相同值的区域压缩成一块,否则它就直接退化成了普通的网格结构,失去了数据结构本身的价值。
这题看着像是在考树的操作,实际上,它考的是两个核心逻辑判断:叶子节点能否提前终结递归,以及递归结果能否重新合并。只要处理这两个点的顺序不乱,代码结构就不会散架。
在准备面试求职时,这一类题目不仅能考察递归的熟练度,更能看出候选人对数据结构本质的理解程度。如果你也有类似的解题心得,不妨常来云栈社区和我们一起聊聊,那里聚集了很多热爱深挖技术细节的极客。