程序员老鬼

公司裁员名单下来了,那个36岁的工程师稳如泰山,反而几个28岁的年轻人走了。

刚看到个贴子,说公司裁员名单出来了,结果那个36岁的工程师稳得一批,反倒走了几个28岁的年轻人。怎么说呢,这事我一点都不意外。

Image

网友的回帖我也瞄了瞄,有说“年龄歧视不存在吗”的,也有说“公司当然选能扛事的”。

但我觉得关键点不是年龄,而是可替代性。有些年轻人确实冲劲足,但业务没沉淀、项目没人记得、走了也不影响流水线,那在裁员里就特别被动。

反倒是那个36岁的工程师,八成是手里有核心模块、出了问题能顶得住,或者在团队里属于“缺了就会塌一角”的那类人。

换个角度讲,职场就像坐地铁,高峰期你挤得再用力,只要没站稳、没扶手,一刹车你还是最先被甩出去。真正稳的人,从来不是因为“年龄大”,而是因为站得住。【备注:文末可领最新资料】

面试题:数组列表中的最大距离

就拿这道「数组列表中的最大距离」聊聊哈,Java 版本的。

题目大概是这样的:

  • 给你一个 List<List<Integer>> arrays

  • 里面每个小数组都是升序排好的,比如:

    • [1, 2, 3]
    • [4, 5]
    • [1, 2, 3, 4]
  • 你要从两个不同的数组里各挑一个数出来,一个是 a,一个是 b

  • 目标是让 |a - b| 尽可能大,返回这个最大值

注意两个点:

  1. 一定要是不同的数组,不能同一个数组里选两个数
  2. 每个数组是有序的,这是关键优化点

最笨的想法:硬算

最直觉的做法就是:

  • 枚举两个数组 i、j
  • 再算它们之间能形成的最大距离
  • 再在所有组合里取最大

但你会发现,有序数组里能形成最大距离的,一定是:

  • 这个数组的最小值(第一个元素)
  • 和另一个数组的最大值(最后一个元素)

所以两两数组之间的最大距离,其实就是这两个候选:

  • abs(min_i - max_j)
  • abs(max_i - min_j)

问题是,如果你真的两两数组去算,时间复杂度是 O(n^2),数组多一点就爆了。

利用「有序」这件事来优化

既然每个数组都是升序的,那每个数组我们只关心两件事:

  • 最小值:第一个元素 first
  • 最大值:最后一个元素 last

接下来想象一下我们从左到右遍历这些数组:

  • 维护一个「目前为止见过的全局最小值」globalMin

  • 维护一个「目前为止见过的全局最大值」globalMax

  • 遍历到当前数组 cur 时:

    • 它的最小值是 curMin
    • 它的最大值是 curMax

那当前数组能和前面那些数组组成的最大距离有两种情况:

  1. 用前面数组里的最小值,配上当前数组的最大值

  • 也就是:curMax - globalMin
  • 用前面数组里的最大值,配上当前数组的最小值

    • 也就是:globalMax - curMin

    这俩取大的,拿来更新答案。

    为什么这样不会踩到「同一个数组」的坑?

    • 因为 globalMin 和 globalMax 都是「之前数组」维护出来的
    • 当前数组是后来的,肯定不是同一个
    • 所以天然保证了「来自不同数组」

    最后别忘了:

    • 每处理完一个数组,要把:

      • globalMin = min(globalMin, curMin)
      • globalMax = max(globalMax, curMax)
    • 这样后面的数组也能用上它

    时间复杂度就是一次线性扫描:O(n),只用常数额外空间。

    import java.util.List;

    publicclassMaxDistanceInArrays{

    /**
         * 数组列表中的最大距离
         * @param arrays 每个内部数组都是升序的
         * @return 最大的 |a - b|,a 和 b 来自不同数组
         */

    publicintmaxDistance(List<List<Integer>> arrays){
    if (arrays == null || arrays.size() < 2) {
    // 题目一般不会这样出,但防一手
    return0;
            }

    // 初始化,用第一个数组的首尾
            List<Integer> firstArray = arrays.get(0);
    int globalMin = firstArray.get(0);                             // 当前见过的最小值
    int globalMax = firstArray.get(firstArray.size() - 1);         // 当前见过的最大值

    int ans = 0;

    // 从第二个数组开始遍历
    for (int i = 1; i < arrays.size(); i++) {
                List<Integer> cur = arrays.get(i);
    int curMin = cur.get(0);
    int curMax = cur.get(cur.size() - 1);

    // 当前数组和前面所有数组能形成的最大距离
    int dist1 = Math.abs(curMax - globalMin);
    int dist2 = Math.abs(globalMax - curMin);

                ans = Math.max(ans, Math.max(dist1, dist2));

    // 更新全局最小值和最大值,给后面的数组用
                globalMin = Math.min(globalMin, curMin);
                globalMax = Math.max(globalMax, curMax);
            }

    return ans;
        }
    }

    你可以顺手带个小例子在 main 里测一下,比如:

    • [[1,2,3],[4,5],[1,2,3,4]]

      • 全局最小一路都是 1
      • 全局最大最终是 5
      • 最大距离就是 5 - 1 = 4

    其实就一句话:有序数组就看两头。 把「所有数组里的最小」和「所有数组里的最大」用一遍扫描串起来,中间只要确保不是同一个数组就行了,而我们从左往右维护,就顺带把这个条件也满足了。

    这个题写顺了之后,以后再看到那种「有序数组 + 距离最大」的,大概率也是同一类套路。后面你要是想顺带看下「最大 j - i 且 A[j] ≥ A[i]」那个版本,也可以再搞一篇完全不一样的解法。

    -END-

    我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html

    最后给大家分享一份不错的副业资料,点击下方公众号,回复关键字: 副业 领,也可以链接我微信:hls404