Python技术迷

准备给一个34岁员工降薪50%,结果发现他刚离婚没软肋了,就把矛头转向了另一个31岁,刚结婚准备要小孩的

刚看到个贴子,说公司本来想给一个34岁员工降薪50%,结果发现人家刚离婚、没软肋了,反倒是另一个31岁、刚结婚准备要娃的倒了霉。

Image

我觉得这事吧…很典型的“职场拿软柿子捏”。网友们的回复我看了看,有说领导现实的,也有骂公司缺德的,我倒觉得问题的根本是:在一些地方,员工的生活状态真的会被当成评估风险的“信息”。谁更不敢走,谁就更容易被压。

换个角度想,领导不是突然坏,而是算计得太清楚。一个刚离婚的,说不定更拼;一个准备生娃的,被默认“不敢辞职”。说白了,这不是道德问题,是性价比思维。

但话说回来,公司再怎么算,也别忘了人心是流动的。今天你看准别人“有软肋”,明天别人也能找到更好的去处。职场关系,说到底还是互相选择。

总的来说吧,时代变了,员工也别把自己当“被拿捏的那一个”。提升价值、稳住心态,不让别人轻易抓住把柄,才是最实际的底气。

面试题:设计循环队列

先上结论:循环队列本质上就是“用一块固定长度的数组,假装自己是个无限头尾相接的队列”,靠下标取模来绕圈,再配合几个指针把状态维护好就行了。

一、为啥要折腾个“循环”队列?

先想一个简单场景: 比如你在写一个日志缓冲区,只想保留最近 N 条;或者一个限流窗口,只需要一个固定长度的队列。这个时候如果用普通的 list 做 pop(0),时间复杂度是 O(n),元素一多就明显卡。

那我能不能:

  • 提前开一个固定大小的数组,比如 5 个格子;
  • 有人进队,就往后一个格子放;
  • 有人出队,就把头往后挪一下,不真的删掉元素;
  • 当尾巴走到数组末尾,再从下标 0 继续走一圈?

这就是循环队列的核心动机:**省空间、不搬家,所有操作都 O(1)**。

二、循环队列怎么设计比较顺手?

我们用数组实现一个“固定容量”的队列,最常见的设计有两种:

  1. 用两个指针 + 额外的 size
  2. 用两个指针 + 空一个格子区分空和满

一般写算法题,我更推荐第 1 种:多一个 size 字段,逻辑更直观,不用纠结空满怎么区分。

我们来定几个状态变量:

  • self.data: 固定长度的列表,长度是 k
  • self.head: 指向队头元素的下标
  • self.tail: 指向“下一个要写入位置”的下标(注意不是最后一个元素)
  • self.size: 当前队列里真实元素个数
  • self.capacity: 容量 k

这样好处是:

  • 队列空:size == 0
  • 队列满:size == capacity
  • 每次移动下标都用取模:(index + 1) % capacity,就自然绕回去了

再看几个操作的含义:

  • enQueue(value):

    • 如果已满,返回 False
    • 把值写到 tail 位置
    • tail = (tail + 1) % capacity
    • size += 1
  • deQueue():

    • 如果为空,返回 False
    • 不用真的删,只要把 head 往后挪
    • head = (head + 1) % capacity
    • size -= 1
  • Front():空就返回 -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

你可以简单跑几个步骤在脑子里过一下:

  1. 初始化容量 3,head=0, tail=0, size=0

  2. 连续 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