程序员老鬼

昨天面了一个比我大8岁的前辈,某大厂前P7,曾经年薪百万的大佬。但他坐在我对面谈薪资时那种小心翼翼的眼神,让我心里特别不是滋味。

刚看到个贴子,说一位网友去面试,面前坐着的是比他大八岁的前大厂P7。聊到薪资,对方小心翼翼地说自己20k就行,不挑活也能加班,因为已经失业快一年了。网友说,那一刻像看见了七年后的自己。

Image

我觉得这画面挺扎心,但真不意外。红利没了,“大佬”跟不上节奏,很快就会被市场抛下。评论区很多人感慨大厂光环不值钱了,我倒觉得,真正值钱的还是你有没有持续拿出结果的能力,别把头衔当护身符。

换个角度看,这位前辈起码愿意放下面子、低预期求一份工作。普通人在职场别神话平台和title,趁还有机会多存点钱,多练几门吃饭的本事。风向怎么吹我们管不了,但只要愿意弯腰、肯更新自己,被卷来卷去,也总能再站起来。

面试题:盛最多水的容器

昨天晚上十一点多,我正准备关电脑回家,我们组那个小李突然蹭过来一句:“东哥,你会那个装水的题不?就两个竖线能装最多水那个。”我一看他那表情,就知道八成又是被算法题支配的恐惧了 😂

先说下题目是啥哈,别一上来就上代码。题目大概是这样: 给你一个 int 数组 height,每个下标 i 上是一根竖着的柱子,高度是 height[i],宽度统一算 1。你可以随便选两根柱子当容器的两边,中间当底,能装的水就是:

  • 宽度:两根柱子之间的距离 j - i
  • 高度:取矮的那根 min(height[i], height[j])

面积 = 宽度 × 高度,你要找最大的那一组面积是多少。

小李一开始想得特别直接:暴力呗,两个 for 循环全算一遍:

publicintmaxAreaSlow(int[] height){
if (height == null || height.length < 2) return0;
int n = height.length;
int ans = 0;
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
int h = Math.min(height[i], height[j]);
int w = j - i;
            ans = Math.max(ans, h * w);
        }
    }
return ans;
}

看着挺对,是不是,对吧?问题是,时间复杂度 O(n²),面试官一看你这个解法,脸上笑嘻嘻,心里 MMP。数组长度一上来 1e5,直接超时,连测试数据都跑不完。

我就跟他说,这题其实是典型的“双指针思路”,写出来就一眼看上去很秀那种,但原理一点也不玄学。

你可以脑补一下: 把所有柱子画在一条直线上,我们先别装什么“聪明”,就先把最左边和最右边两根柱子拿来当边界。

  • 左指针 left = 0
  • 右指针 right = n - 1

这时候能装的水面积是多少?area = (right - left) * min(height[left], height[right])

重点来了:下一步你要怎么动指针?

很多人会犹豫:我是不是要尝试所有可能?其实不用。 有一个特别关键的观察:

每一步,只移动“矮的那一边”的指针,才有可能变大;移动高的那边,只会更糟。

为啥呢,我用很口水的话说一下哈:

  • 你把两根柱子固定住,面积 = 底 × 高

  • 你往中间移动任意一边,底一定变小(距离变近了)

  • 想让面积变大,只能指望“高变大”能弥补“底变小”

  • 那你想想:

    • 底变小 ✔
    • 高度还得由更矮的那根决定(因为高度取 min 啊)
    • 也就是说,新的高度不会比原来的高
    • 那面积就是:底变小,高不变或更矮,只会更小
    • 如果你动的是那根更高的柱子

    • 所以,动高的那边完全没意义

所以,每一步我们都只做一件事:

  • height[left] < height[right] 👉 左边更矮,那就 left++
  • 否则 👉 right--

一路两头往中间夹,顺便更新一下当前最大面积就行了。

代码其实非常短,小李看完直接“卧槽这也太简洁了”:

publicclassSolution{

publicintmaxArea(int[] height){
if (height == null || height.length < 2) {
return0;
        }
int left = 0;
int right = height.length - 1;
int ans = 0;

while (left < right) {
int h = Math.min(height[left], height[right]);
int w = right - left;
int area = h * w;
if (area > ans) {
                ans = area;
            }

// 关键的移动策略:永远移动较矮的那一边
if (height[left] < height[right]) {
                left++;
            } else {
                right--;
            }
        }

return ans;
    }

// 随便写个 main 方便你本地跑一下
publicstaticvoidmain(String[] args){
        Solution s = new Solution();
int[] height = {1, 8, 6, 2, 5, 4, 8, 3, 7};
        System.out.println(s.maxArea(height)); // 输出 49
    }
}

你要是还不太有感觉,可以手动在纸上走几步,比如上面这个经典数组:

  • 一开始:left=0 高 1,right=8 高 7

    • 面积 = min(1, 7) * (8 - 0) = 1 * 8 = 8
    • 左边更矮,所以 left++
  • 然后:left=1 高 8,right=8 高 7

    • 面积 = min(8, 7) * 7 = 7 * 7 = 49 → 目前最大值
    • 右边更矮,right--
  • 再往里缩,面积会一会儿变大一会儿变小,但 49 就一直保留到最后

中间具体每一步就不全算了,你哪天在地铁上无聊,可以自己拿手机记事本算一算,挺解压的。

-END-

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