跟一个年薪100万的技术总监吃饭,席间聊到年底绩效评级,他说决定谁拿A+、谁升职,只看一个指标,这人的“静音能力”。
刚看到个贴子:网友和年薪百万的技术总监吃饭,聊到年底绩效。总监说,谁拿A+谁升职,他只看一个指标——“静音能力”,不看加了多少班。听完的确有点发凉。
网友回帖,有人夸这是情绪稳定,有人骂是培养“哑巴工具人”。我觉得,两边都没说到点上。
在我看来,这个“静音”,其实考的是情绪管理和边界感:能不能少抱怨、多解决问题,不在群里天天输出负能量,也不会一点不公就当场炸裂。但如果公司只爱安静、不鼓励合理发声,那迟早问题堆成雷。
总的来说吧,职场确实需要“静音能力”,但更需要“清晰表达”的能力。既能稳住情绪,又敢在关键时刻说真话的人,才是真正值得被重用的。
面试题:缺失的区间
我跟你说,缺失的区间这个题啊,真的是那种,看着不难,写起来容易翻车的那种。 我前两天在公司楼下喝咖啡,陪我们组那个小李刷题,他面试前临时抱佛脚,就把这道拿出来问我:“哥,这玩意儿到底咋优雅地写?”
题目大概意思就是:
给你一个有序、不重复的整型数组 nums,还有一个整体区间 [lower, upper],让你把在这个大区间里、但没出现在数组里的那些连续区间找出来,比如:
lower = 0, upper = 99nums = [0, 1, 3, 50, 75]
那缺的就是:2、4->49、51->74、76->99 这几段。 返回的时候一般用字符串表示,比如 "2"、"4->49" 这种。
当时小李第一反应是那种,暴力:从 lower 一路扫到 upper,用个 set 看每个数字在不在数组里。 我说你这思路当然也能做,就是——很费劲,数据大一点你就知道啥叫超时了,对吧。
比较舒服的写法,其实就一个思路:把“当前数字”变成“前一个端点”,遍历一遍数组顺带把空档区间捡出来。
我给他在纸上写了个小图: 想象一下我们在 [lower, upper] 外面,各往外再扩一个点:左边设成 lower - 1,右边设成 upper + 1,这俩是“哨兵”。 然后我们从左往右看:前一个数是 prev,当前看到的是 cur,只要 **cur - prev >= 2**,说明中间至少空了一个数,这一段就是缺失区间。
比如刚才那个例子:
一开始 prev = lower - 1 = -1第一个数 cur = 0,差值0 - (-1) = 1,没空档第二个数 cur = 1,1 - 0 = 1,也没空档第三个数 cur = 3,3 - 1 = 2,有空档,说明少了[2,2],就是"2"…… 数组扫完以后,再和 upper + 1比一遍,把最后一段也补上
整件事就变成:用一个 prev 挂住“上一个存在的点”,每次看当前点和它之间有没有间隔。
小李当时一脸恍然:“原来连 for 循环都不用倒来倒去的那种?”
我顺手给他写了个 Java 版本,你可以直接拿去用,逻辑挺清爽的:
import java.util.ArrayList;
import java.util.List;
publicclassMissingRanges{
public List<String> findMissingRanges(int[] nums, int lower, int upper){
List<String> res = new ArrayList<>();
// 用 long 是为了防止 lower = Integer.MIN_VALUE 时 lower - 1 溢出
long prev = (long) lower - 1;
long cur;
// 多加一个“虚拟元素”:upper + 1,方便统一处理最后一段
for (int i = 0; i <= nums.length; i++) {
if (i == nums.length) {
cur = (long) upper + 1;
} else {
cur = nums[i];
}
// 中间至少隔了一个数,说明有缺失区间
if (cur - prev >= 2) {
res.add(formatRange(prev + 1, cur - 1));
}
prev = cur;
}
return res;
}
// 把 [start, end] 格式化成题目要的字符串形式
private String formatRange(long start, long end){
if (start == end) {
return String.valueOf(start);
} else {
return start + "->" + end;
}
}
// 简单测一下
publicstaticvoidmain(String[] args){
MissingRanges m = new MissingRanges();
int[] nums = {0, 1, 3, 50, 75};
List<String> ans = m.findMissingRanges(nums, 0, 99);
System.out.println(ans); // [2, 4->49, 51->74, 76->99]
}
}
这个实现里有几个小细节,小李当时没注意到,我特地给他说了一遍,你也顺带看下:
第一个,那个 prev 我是用 long 存的,不是 int。为啥? 因为假如测试数据给你 lower = Integer.MIN_VALUE,你要是直接 lower - 1,就溢出了,结果一大堆奇怪的 bug,让人怀疑人生。 转成 long 就老老实实了。
第二个,把 upper + 1 当做“虚拟元素”塞到循环里。 这样你不需要在循环外再写一段“处理尾巴”的代码,否则你得先循环处理数组,再单独判断一下 upper 后面还有没有缺失,很啰嗦。 多加一个虚拟点,所有逻辑都长得一样,整个 for 写完直接收工。
第三个,formatRange 那里分成“单点”和“区间”两种情况:
当起点等于终点,就说明只缺一个数,比如缺 2,就输出"2";当不等,就输出 "start->end",比如"4->49"。
面试官一般会看你这块边界处理是不是干净,不多空格、不乱写。
时间复杂度这题就很好说了:整个数组只过一遍,O(n),空间就是返回列表那点东西,额外 O(1)。 我当时跟小李说:这种题就是用一个变量把“之前的信息”记住,不要每次都从头算,这种思想比这道题本身更重要。
对了,那天聊完这个,我们又扯到以前写数据库和 MQ 那些踩坑故事,思路其实都差不多:一边看日志、一边盯关键“断点”,找中间缺的那一段,你看问题很多都是同一个味道,只是壳子不一样而已。
行,先这样,我去给小李催一下简历进度,他这两天又开始摸鱼了……
-END-
我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html