准备给一个34岁员工降薪50%,结果发现他刚离婚没软肋了,就把矛头转向了另一个31岁,刚结婚准备要小孩的
刚看到个贴子,说公司本来想给一个34岁的员工降薪50%,结果发现人家刚离婚、没软肋了,转头就把矛头对准另一个31岁、刚结婚准备要孩子的。
网友们的回复我看了看,有的骂领导坏,有的嘲讽“职场就是看谁更容易捏”。我部分认同,但我的看法是——这事其实反映了一个赤裸现实:有些老板不是看绩效,是看你“能不能被拿捏”。你一旦被认为有家庭压力、有贷款、有牵绊,就很容易被盯上。
换个角度想,企业要控制成本可以理解,但把成本压在最弱的那个人身上,这就不是管理,是欺负老实人。
说到底还是价值和边界的问题——你越让人觉得你离不开这份工作,对方越容易拿你开刀。
不过话说回来,我们普通人也别被吓得太悲观。能做的就是提升自己的选择权,明确底线,让别人知道你不是任捏的软柿子。
公司可以挑员工,员工也能挑公司。【备注:文末可领最新资料】
面试题:设计循环队列
想象一下,你在做一个“消息通知”功能,后端要暂存最近 100 条消息,用完就扔,新的来了就顶掉最早的那条。这个时候要是用最普通的数组队列,每次出队都把后面的元素整体往前挪一遍,时间一长 CPU 得被你气冒烟,对吧。这个时候循环队列就上场了。
先说下循环队列是个啥 你可以脑补一圈座位,固定就 5 个位置:0、1、2、3、4。新来的同学从 tail 坐下,走的同学从 head 离开。坐到 4 的时候再来人,就绕回 0 继续用,所以叫“循环”。
用数组实现的时候,一般有两种做法: 1)留一个空位,通过 head == tail 表示空, (tail + 1) % n == head 表示满。 2)再加一个 size 变量,直接用 size == 0 判断空,size == capacity 判断满。
我下面用第二种,逻辑好理解一点,也不容易绕晕。
大致设计长这样:
用一个 int[] data存元素int head指向队头元素下标int tail指向“下一个要放元素”的位置int size当前有多少元素int capacity队列容量(数组长度)
公式就记两个:
入队之后: tail = (tail + 1) % capacity出队之后: head = (head + 1) % capacity
取模就是那个“绕圈”的关键。
我先把 Java 代码丢出来,你可以对着看:
publicclassMyCircularQueue{
privatefinalint[] data;
privatefinalint capacity;
privateint head; // 指向队头元素
privateint tail; // 指向下一个要写入的位置
privateint size; // 当前元素个数
publicMyCircularQueue(int k){
if (k <= 0) {
thrownew IllegalArgumentException("容量必须 > 0");
}
this.capacity = k;
this.data = newint[k];
this.head = 0;
this.tail = 0;
this.size = 0;
}
// 入队:成功返回 true,满了返回 false
publicbooleanenQueue(int value){
if (isFull()) {
returnfalse;
}
data[tail] = value;
tail = (tail + 1) % capacity;
size++;
returntrue;
}
// 出队:成功返回 true,空队列返回 false
publicbooleandeQueue(){
if (isEmpty()) {
returnfalse;
}
// 实际上不用把 data[head] 清零,逻辑上它已经“无效”了
head = (head + 1) % capacity;
size--;
returntrue;
}
// 取队头元素,空队列就返回 -1(也可以改成抛异常,看题目要求)
publicintFront(){
if (isEmpty()) {
return -1;
}
return data[head];
}
// 取队尾元素,注意 tail 指向的是“下一个要写的位置”
publicintRear(){
if (isEmpty()) {
return -1;
}
int index = (tail - 1 + capacity) % capacity;
return data[index];
}
publicbooleanisEmpty(){
return size == 0;
}
publicbooleanisFull(){
return size == capacity;
}
}
代码不长,但里面几个小坑,顺手说一下。
第一个容易绕的点:head 和 tail 的含义 我这里的约定是:
head永远指向当前队头元素tail永远指向“下一个入队要写入的位置”
所以:
入队的时候在 tail写数据,然后tail往后挪出队的时候 head往后挪,相当于把原来的队头“丢弃”了
这样好处是,data 里某个位置到底有没有值,其实不重要,我们只看 head、tail 和 size,逻辑会简单很多。
第二个点:为什么 Rear 要 (tail - 1 + capacity) % capacity因为 tail 指的是下一个要写的位置,比如容量是 5,当前已经塞了 3 个元素:
假设依次入:10, 20, 30
第一次入队:data[0] = 10,tail -> 1 第二次入队:data[1] = 20,tail -> 2 第三次入队:data[2] = 30,tail -> 3
你要取队尾,就应该拿 data[2],也就是 tail - 1。 但会有绕圈的情况,比如 tail == 0 的时候,再减一就是 -1,这会越界,所以要先加上 capacity 再取模,保证结果落在 [0, capacity-1] 之间。
第三个点:为啥需要 size 如果只看 head / tail,你会发现有个尴尬的情况:
刚创建的时候: head = 0, tail = 0,这显然是“空队列”如果你正好把数组填满,继续绕了一圈,最后 head也可能等于tail
这时候只看 head == tail 完全分不清是“空”还是“满”。 常见的两个解决办法:
要么强制保留一个空位,永远不把数组塞满 要么像我这样,多维护一个 size
size 的判断就很直观:
size == 0:空size == capacity:满
代价就是每次入队 / 出队要多 ++ / -- 一下,不过这点开销基本可以忽略。
再顺嘴说点使用场景 循环队列这种东西,在实际项目里挺常见的,尤其是那种容量固定、不断覆盖旧数据的场景,比如:
日志系统里“只保留最近 N 条记录”的内存缓冲 限流、滑动窗口统计最近 N 秒的请求数 音视频、设备采集这类场景的环形缓冲区
这时候如果你还用普通数组每次挪一长串数据,时间一长性能肯定难看。
最后简单小结一下哈: 核心就三个点:
用数组当底层存储,空间连续、访问快。 用 head、tail、size三个变量维护状态,通过取模实现“绕圈”。注意 Rear的下标和空 / 满的判定逻辑,不要被各种边界条件绊住。
你可以先把这段 Java 代码直接丢到 IDE 里跑两组简单的入队 / 出队测试,多打几个打印,看着指针怎么走,很快就完全熟了。
-END-
我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html