面试官:线上突然大量报错,你先查什么? 我:先查今天谁发了版
线上突然一片报错,我第一反应通常不是冲进代码里找 if else。
先看今天谁发了版。
这话听着像段子,其实一点不玄学。线上大量报错这种事,最怕一上来埋头翻业务代码,翻半小时,最后发现只是 10 分钟前刚发过版,某个配置没带上,某个开关值写反了,或者一个本地没问题、线上才炸的默认参数被顺手改了。
排查顺序我一般很土:先看发布记录,再看监控告警,再捞错误日志和 trace。 发布时间和报错时间能不能对上,这是第一刀。能对上,先怀疑变更;对不上,再去看流量、依赖服务、数据库、缓存、MQ 这些基础设施。
很多人觉得“先查谁发版”是在甩锅。我倒不这么看。线上排障不是写复盘报告,先缩小范围最重要。一次大面积报错,八成不是代码凭空抽风,要么变更,要么依赖抖了,要么配置动了
算法题:连续空余座位
凌晨两点,刷题群里丢了个题目:连续空余座位。看着像模拟,其实坑点全在“连续”两个字上。要的不是空座位个数,是能不能找到一段连续的、长度满足条件的空位区间。
题目抽象一下:给一个数组,1 表示已占用,0 表示空位。问是否存在长度为 k 的连续 0。
我第一眼就不太信那种“遍历一遍统计总数”的写法。连续问题,最忌讳的就是把它写成计数问题。只要中间断一次,前面的就作废。
直接上代码,核心逻辑就几行:
publicclassSeatFinder{
publicbooleanhasContinuousSeats(int[] seats, int k){
int count = 0;
for (int i = 0; i < seats.length; i++) {
if (seats[i] == 0) {
count++;
if (count >= k) {
returntrue;
}
} else {
count = 0; // 一旦被打断,重新开始
}
}
returnfalse;
}
}
就这么简单?对,连续问题,往往就是这种“滚动计数 + 断点清零”。
但实际写业务的时候,数组往往不是给你现成的 0/1,而是一堆座位对象,比如:
classSeat{
privateint row;
privateint col;
privateboolean occupied;
publicbooleanisOccupied(){
return occupied;
}
}
那你就别再额外构造一个 int[] 了,直接按顺序扫。
publicbooleanhasContinuousSeats(List<Seat> seatList, int k){
int count = 0;
for (Seat seat : seatList) {
if (!seat.isOccupied()) {
count++;
if (count == k) {
returntrue;
}
} else {
count = 0;
}
}
returnfalse;
}
注意一个细节:这里用 == k 也行,用 >= k 也行。但如果题目是“刚好 k 个”,那判断条件就得改。很多人写题不仔细,这里容易被反杀。
再往下想一步,如果题目升级——不只是判断有没有,而是要返回那段连续区间的起始下标呢?
这时候就不能只记 count 了,还得记起点。
publicintfindStartIndex(int[] seats, int k){
int count = 0;
int start = -1;
for (int i = 0; i < seats.length; i++) {
if (seats[i] == 0) {
if (count == 0) {
start = i; // 记录连续段起点
}
count++;
if (count == k) {
return start;
}
} else {
count = 0;
start = -1;
}
}
return -1;
}
这里我习惯在 count == 0 的时候记录 start,而不是等到满足 k 再去倒推 i - k + 1。两种都行,但前者在调试时更直观,尤其是你想打日志看连续区间的时候。
再说个容易被忽略的点:如果座位是按“排”来给的,比如二维数组,每一排单独算连续,中间不能跨排。
那就别偷懒 flatten 成一维。直接按行处理:
publicbooleanhasContinuousSeats2D(int[][] seats, int k){
for (int row = 0; row < seats.length; row++) {
int count = 0;
for (int col = 0; col < seats[row].length; col++) {
if (seats[row][col] == 0) {
count++;
if (count >= k) {
returntrue;
}
} else {
count = 0;
}
}
}
returnfalse;
}
这种题目,说白了就是状态机。只有两个状态:当前连续长度是多少,被没被打断。
别一上来就想着什么滑动窗口模板。滑动窗口适合“固定长度区间求和”“最长子串”那类问题。这个题不需要左右指针来回动,一个指针够了。
我见过有人写成两层循环,外层枚举起点,内层检查后面 k 个是不是 0。时间复杂度直接从 O(n) 退化成 O(nk)。数据量一大,直接凉。
连续问题的套路就一句话:顺序扫描,遇到断点立即清零。别留恋。
很多算法题,其实不是难在思路,而是难在你会不会在第一时间识别“它本质是个连续计数问题”。一旦识别出来,代码就该像上面这样干净,不绕。
写到这就够了。连续这两个字,看着简单,写错的人比你想的多。