Python技术迷

某大厂员工吐槽:直接用git pull拉代码,被diss!

一个大厂员工说,自己平时拉代码一直顺手 git pull,结果干活干着干着,大群突然有人点名他,说你这样不行,会把提交历史搞得一团糟,后面排查问题都费劲。

他人都懵了。

Image

不过也能理解为啥有人急。团队协作里,有些人特别在意提交线要干净,能 rebase 就别 merge,看记录像看地铁线路图一样,最好一条直线到底。

但问题是,公司里很多规范真不是写在门口的。没人讲,没人培训,默认大家都懂。等你踩到了,才在大群里补一刀。

程序员老鬼只能说,技术债有时候不是代码写出来的,是沟通省出来的。下次别光 diss,顺手把规范文档甩出来,大家都省心。


算法题:二叉树的直径

二叉树的直径,别上来就数节点

这题坑不在代码长,坑在“直径”这两个字看着太像根节点两边相加。

我第一次看这种题,第一反应也会往根节点上凑:左子树高度 + 右子树高度,不就完了么?

不一定。

直径可能经过根节点,也可能压根不经过根节点。比如某个左子树里面自己长得很歪,它内部两条链加起来,比经过整棵树根节点还长。

所以这题不能只在根节点算一次,得在每个节点都算一次。

我一般这么判断:

对任意一个节点来说,经过它的最长路径就是:

左子树高度 + 右子树高度

然后全局取最大值。

这里要注意,很多题里的直径指的是“边数”,不是“节点数”。所以空节点高度返回 0,叶子节点高度返回 1,这样左右高度相加,刚好就是边数。

代码可以写得很短,不需要搞一堆花活。

classSolution{

privateint maxPath = 0;

publicintdiameterOfBinaryTree(TreeNode root){
        calcHeight(root);
return maxPath;
    }

privateintcalcHeight(TreeNode node){
if (node == null) {
return0;
        }

int leftHeight = calcHeight(node.left);
int rightHeight = calcHeight(node.right);

int passThisNode = leftHeight + rightHeight;
if (passThisNode > maxPath) {
            maxPath = passThisNode;
        }

return Math.max(leftHeight, rightHeight) + 1;
    }
}

树节点大概长这样:

classTreeNode{
int val;
    TreeNode left;
    TreeNode right;

    TreeNode(int val) {
this.val = val;
    }
}

这段代码里有两个东西别混了。

calcHeight 返回的是“当前节点往下能走多深”。

maxPath 记录的是“目前见过的最长直径”。

比如走到某个节点,它左边高度是 3,右边高度是 2,那经过这个节点的路径长度就是 5。这个 5 不一定就是最终答案,但要拿出来跟全局最大值比一下。

然后函数返回给父节点的时候,不能返回 3 + 2。

因为父节点只能选你这棵子树里的一条链继续往上接,不可能左边右边都要。这里是很多人写错的地方。

所以返回值只能是:

Math.max(leftHeight, rightHeight) + 1

看一个简单的树:

        1
       / \
      2   3
     / \
    4   5

在节点 2 上,左高度是 1,右高度是 1,经过它的路径是 2。

在节点 1 上,左高度是 2,右高度是 1,经过它的路径是 3。

最后答案就是 3,对应路径可以是:

4 -> 2 -> 1 -> 3

这里数的是边,不是节点。节点有 4 个,边是 3 条。

复杂度也没什么悬念,每个节点只访问一次,时间复杂度 O(n)。递归栈和树高有关,最坏链表形状是 O(n),平衡一点就是 O(log n)。

这题写到最后,其实就一句话:后序遍历算高度,顺手更新每个节点作为拐点时的最大路径。

别只盯着根节点,根节点经常只是个路过的。