准备给一个34岁员工降薪50%,结果发现他刚离婚没软肋了,就把矛头转向了另一个31岁,刚结婚准备要小孩的
刚看到个贴子,说公司本来想给一个34岁员工降薪50%,结果发现人家刚离婚、没软肋了,反倒是另一个31岁、刚结婚准备要娃的倒了霉。
我觉得这事吧…很典型的“职场拿软柿子捏”。网友们的回复我看了看,有说领导现实的,也有骂公司缺德的,我倒觉得问题的根本是:在一些地方,员工的生活状态真的会被当成评估风险的“信息”。谁更不敢走,谁就更容易被压。
换个角度想,领导不是突然坏,而是算计得太清楚。一个刚离婚的,说不定更拼;一个准备生娃的,被默认“不敢辞职”。说白了,这不是道德问题,是性价比思维。
但话说回来,公司再怎么算,也别忘了人心是流动的。今天你看准别人“有软肋”,明天别人也能找到更好的去处。职场关系,说到底还是互相选择。
总的来说吧,时代变了,员工也别把自己当“被拿捏的那一个”。提升价值、稳住心态,不让别人轻易抓住把柄,才是最实际的底气。
面试题:设计循环队列
先上结论:循环队列本质上就是“用一块固定长度的数组,假装自己是个无限头尾相接的队列”,靠下标取模来绕圈,再配合几个指针把状态维护好就行了。
一、为啥要折腾个“循环”队列?
先想一个简单场景: 比如你在写一个日志缓冲区,只想保留最近 N 条;或者一个限流窗口,只需要一个固定长度的队列。这个时候如果用普通的 list 做 pop(0),时间复杂度是 O(n),元素一多就明显卡。
那我能不能:
提前开一个固定大小的数组,比如 5 个格子; 有人进队,就往后一个格子放; 有人出队,就把头往后挪一下,不真的删掉元素; 当尾巴走到数组末尾,再从下标 0 继续走一圈?
这就是循环队列的核心动机:**省空间、不搬家,所有操作都 O(1)**。
二、循环队列怎么设计比较顺手?
我们用数组实现一个“固定容量”的队列,最常见的设计有两种:
用两个指针 + 额外的 size 用两个指针 + 空一个格子区分空和满
一般写算法题,我更推荐第 1 种:多一个 size 字段,逻辑更直观,不用纠结空满怎么区分。
我们来定几个状态变量:
self.data: 固定长度的列表,长度是kself.head: 指向队头元素的下标self.tail: 指向“下一个要写入位置”的下标(注意不是最后一个元素)self.size: 当前队列里真实元素个数self.capacity: 容量 k
这样好处是:
队列空: size == 0队列满: size == capacity每次移动下标都用取模: (index + 1) % capacity,就自然绕回去了
再看几个操作的含义:
enQueue(value):如果已满,返回 False 把值写到 tail位置tail = (tail + 1) % capacitysize += 1deQueue():如果为空,返回 False 不用真的删,只要把 head往后挪head = (head + 1) % capacitysize -= 1Front():空就返回 -1,不空就data[head]Rear():最后一个元素在tail的前一个位置:rear_index = (tail - 1 + capacity) % capacity
注意 Rear() 那个 +capacity,是为了防止 tail 为 0 时变成 -1,Python 里 -1 虽然能用,但语义上会绕到末尾,跟我们逻辑不太一致,所以最好自己取模。
三、Python 代码实现一遍
按很多面试/刷题平台的习惯,我们把这些方法封装成一个类 MyCircularQueue:
classMyCircularQueue:
def__init__(self, k: int):
"""初始化队列,容量固定为 k"""
self.capacity = k
self.data = [0] * k
self.head = 0# 指向队头元素
self.tail = 0# 指向下一个要写入的位置
self.size = 0# 当前元素个数
defenQueue(self, value: int) -> bool:
"""入队:成功返回 True,失败返回 False"""
if self.isFull():
returnFalse
self.data[self.tail] = value
self.tail = (self.tail + 1) % self.capacity
self.size += 1
returnTrue
defdeQueue(self) -> bool:
"""出队:成功返回 True,失败返回 False"""
if self.isEmpty():
returnFalse
# 其实不需要清空 data[head],逻辑上已经“看不到”它了
self.head = (self.head + 1) % self.capacity
self.size -= 1
returnTrue
defFront(self) -> int:
"""返回队头元素,空队列返回 -1"""
if self.isEmpty():
return-1
return self.data[self.head]
defRear(self) -> int:
"""返回队尾元素,空队列返回 -1"""
if self.isEmpty():
return-1
# tail 指向的是“下一个要写入的位置”,所以要往前挪一位
rear_index = (self.tail - 1 + self.capacity) % self.capacity
return self.data[rear_index]
defisEmpty(self) -> bool:
return self.size == 0
defisFull(self) -> bool:
return self.size == self.capacity
你可以简单跑几个步骤在脑子里过一下:
初始化容量 3,
head=0, tail=0, size=0连续
enQueue(1), enQueue(2), enQueue(3)
最后 head=0, tail=0, size=3(因为 tail 绕了一圈)
这时 isFull() 为 True,不能再加
deQueue() 一次,head=1, tail=0, size=2
再 enQueue(4),会写到 tail=0 的位置,把原来下标 0 的“旧数据”覆盖掉,但逻辑上已经不在队列里了
整个过程,底层数组始终没扩容、没搬家,只是头尾指针在一个圈里绕来绕去。
循环队列的关键点,其实就两条:
用固定数组 + 取模运算,把线性结构变成“首尾相接的环” 用 head / tail / size明确区分空、满和当前队头队尾
只要这两个概念想清楚,代码就不难了。你之后再碰到“固定容量队列”“环形缓冲区”“滑动窗口”之类的题,其实都是在这套模板上稍微改改,就能复用这套思路。
-END-
我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html
虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB