程序员老鬼

遇到裁员,切记不要傻乎乎签字拿n加 1,和公司谈,可以拿到n加 3。你和公司谈判的筹码就是:你可以死活赖着不走,但。。

网友吐槽:如果遇到裁员,切记不要傻乎乎签字拿n加 1,和公司谈,可以拿到n加 3。你和公司谈判的筹码就是:你可以死活赖着不走,但是公司能发的单解个数有限。

最后公司会和你协商n加 3,同时加签一份对赔偿的保密协议。此办法适用于所有互联网大厂。

Image

但网上那种“所有大厂都能谈到 N+3”,也别太信。每家公司情况不一样,每个人岗位、合同、证据也不一样。能谈多少,靠的不是嘴硬,是你手里有没有东西。

所以啊,真遇到裁员,先别急着签字,先把文件拍下来,算清楚账,问明白话。HR笑得再温柔,那也是公司的人。你得先站在自己这边。

面试题:考场就座

考场空着一排座位,学生一个个进来。每次都要坐到离最近的人最远的位置,距离一样就坐编号小的。有人离开,还得把位置空出来。

这题第一眼别急着写数组循环。

数组当然能过一部分,seat() 每次从 0 扫到 n - 1,算每个空位到最近人的距离。代码好写,但调用次数一多就难看了。真正麻烦的不是“找位置”,而是学生离开以后,原来的空区间要重新合并。

我一般会把它看成一堆“空区间”。

比如已经坐了:

0      4          9

那空区间其实是:

(0,4)  (4,9)

每个区间能贡献一个候选座位。

如果左边界是 -1,说明最左边没人,候选位置就是 0。

如果右边界是 n,说明最右边没人,候选位置就是 n - 1。

如果是中间区间 (l, r),候选位置就是:

(l + r) / 2

距离怎么算也要注意。两端区间和中间区间不一样:

privateintgap(int l, int r){
if (l == -1) return r;
if (r == size) return size - 1 - l;
return (r - l) / 2;
}

这地方我见过不少人写错。尤其是左端点没人时,距离不是 (r - l) / 2,而是直接坐 0,距离是 r。

完整代码我会这么写,短一点,别搞一堆花活:

import java.util.*;

classExamRoom{
privatefinalint size;
privatefinal TreeSet<Integer> seated = new TreeSet<>();

publicExamRoom(int n){
this.size = n;
    }

publicintseat(){
if (seated.isEmpty()) {
            seated.add(0);
return0;
        }

int bestSeat = 0;
int bestDist = seated.first();

        Integer prev = null;

for (Integer cur : seated) {
if (prev != null) {
int mid = (prev + cur) / 2;
int dist = mid - prev;

if (dist > bestDist) {
                    bestDist = dist;
                    bestSeat = mid;
                }
            }
            prev = cur;
        }

int rightDist = size - 1 - seated.last();
if (rightDist > bestDist) {
            bestSeat = size - 1;
        }

        seated.add(bestSeat);
return bestSeat;
    }

publicvoidleave(int p){
        seated.remove(p);
    }
}

这版代码没用优先队列,是故意的。

优先队列当然可以做,把每段空区间按距离排序,seat() 直接弹最大区间。但 leave(p) 会把左右两个区间合并,这时候旧区间怎么删就是个麻烦点。要么额外维护两个 Map,要么懒删除。写面试代码时很容易把边界写脏。

用 TreeSet 存已经坐下的位置,逻辑反而稳。

seat() 时只看已经有人坐的位置,顺着扫一遍,相邻两个人之间一定是一段连续空位。这个区间里最优位置就是中点。再单独比较最左边和最右边两个边界。

比如 n = 10:

第一次没人,坐 0。

第二次,右边距离最大,坐 9。

第三次,中间 (0,9),坐 4。

第四次,比较 (0,4) 和 (4,9),前者中点 2,后者中点 6,距离一样,按题意选小的,所以坐 2。

这也是为什么代码里只在 dist > bestDist 时更新,而不是 >=。因为从左往右扫,先遇到的座位编号更小,距离相等时不要覆盖。

时间复杂度也很直观。

seat() 要扫一遍当前已经坐下的人,复杂度是 O(k),k 是当前人数。插入 TreeSet 是 O(log k)。

leave(p) 删除一个座位,是 O(log k)。

如果题目数据特别大,才值得上优先队列版本。普通场景下,这版更像我会在白板上写出来的代码:边界清楚,不绕,不容易在离场合并区间时把自己坑进去。