Python技术迷

程序员一个月 普遍 30-40k,为什么还要去考公拿5-6k,图什么呢

刚看到个贴子,说程序员一个月三四万,还要去考公拿五六千,图啥?

Image

不少网友说是图稳定、图清闲,还有图社会地位的。说实话,我挺理解这种选择的。 

在互联网混久了的人都知道,三四万的工资背后,是通宵写代码、项目延期、被裁的风险。一个bug能毁掉一夜的睡眠,一轮裁员能让你从年薪五十万变失业。考公那五六千虽然少,但胜在可预期,不怕被优化。 

我觉得关键在于——人到某个阶段,追求的就不是钱多钱少,而是安全感。有人要爆发力,有人要续航力,各取所需罢了。 

社会不该用“工资高低”去评判人生路线。程序员考公,不是“降维”,是换个赛道活得更稳。【备注:文末可领最新资料】

面试题:物块放置查询

想象一条数轴,依次往上“扔”一堆正方形木块。第 i 块给你 left 和 size,覆盖区间 [left, left+size),它会落到该区间当前最高高度之上。每放完一块,问此时整体的最高高度是多少。典型输入是一串操作,输出是一串逐步的最高值。

直白点:每次区间取最大、再把这段抬高。

最容易的写法就是维护一堆区间,来一块就线性遍历找重叠、算最高、然后“更新”。这样最坏 O(n²)。n 上万就吃不消了。

提速关键:坐标压缩 + 线段树

这里的坐标可能很大(1e9 级),但真正用到的只有所有块的左右端点。所以:

  1. 把所有端点丢进数组、去重排序,得到稀疏坐标;
  2. 用这些离散点建一棵线段树,支持:
  • 区间最大值查询(问当前这段最高多少);
  • 区间赋值覆盖(把这段抬到新高度)。 每次放块:先查这段最大 base,新高度就是 base + size,再把这段赋成新高度。时间大约 O(n log n)。

小提醒:这里“赋值覆盖”为啥安全?因为我们永远把一个区间整体抬到“原本最大+size”,这一定不小于原高度,之后别的块再来时只看“最大值”即可。

from bisect import bisect_left

classSegmentTree:
def__init__(self, n):
        self.n = n
        self.maxv = [0]*(4*n)
        self.tag = [None]*(4*n)  # 懒标记:区间赋值

def_apply(self, idx, val):
        self.maxv[idx] = val
        self.tag[idx] = val

def_push(self, idx):
if self.tag[idx] isnotNone:
            self._apply(idx*2, self.tag[idx])
            self._apply(idx*2+1, self.tag[idx])
            self.tag[idx] = None

def_pull(self, idx):
        self.maxv[idx] = max(self.maxv[idx*2], self.maxv[idx*2+1])

defupdate(self, L, R, val, idx=1, l=0, r=None):
if r isNone: r = self.n-1
if L<=l and r<=R:
            self._apply(idx, val)
return
        self._push(idx)
        mid = (l+r)//2
if L<=mid: self.update(L, R, val, idx*2, l, mid)
if R>mid:  self.update(L, R, val, idx*2+1, mid+1, r)
        self._pull(idx)

defquery(self, L, R, idx=1, l=0, r=None):
if r isNone: r = self.n-1
if L<=l and r<=R:
return self.maxv[idx]
        self._push(idx)
        mid = (l+r)//2
        ans = 0
if L<=mid: ans = max(ans, self.query(L, R, idx*2, l, mid))
if R>mid:  ans = max(ans, self.query(L, R, idx*2+1, mid+1, r))
return ans

deffalling_squares(positions):
# positions: List[List[int]],每项 [left, size]
# 1) 坐标压缩
    endpoints = []
for L, S in positions:
        endpoints.append(L)
        endpoints.append(L+S)
    xs = sorted(set(endpoints))

# 用半开区间 [L, R) -> 压到索引区间 [li, ri-1]
defidx(x):return bisect_left(xs, x)

    m = len(xs)
# 叶子代表 xs[i]~xs[i+1) 这段;因此有效段是 0..m-2
    st = SegmentTree(m-1)

    ans = []
    cur_max = 0
for L, S in positions:
        li, ri = idx(L), idx(L+S)
        base = st.query(li, ri-1) if li<=ri-1else0
        h = base + S
        st.update(li, ri-1, h)
        cur_max = max(cur_max, h)
        ans.append(cur_max)
return ans

if __name__ == "__main__":
# 示例
    ops = [[1,2],[2,3],[6,1]]
    print(falling_squares(ops))  # -> [2,5,5]

复杂度与坑

  • 复杂度:坐标压缩 O(n log n),每次查询/更新 O(log n),总计 O(n log n)。
  • 坑 1:离散后的“段”数是 len(xs)-1,因为每段代表相邻坐标之间的半开区间。
  • 坑 2:线段树要“区间赋值”不是加法,所以懒标记是覆盖而不是加。
  • 坑 3:如果题目真的要求在线(不知道后续所有操作的端点),可以用“动态开点线段树”或“有序映射 + 区间重分裂”的做法,代码更长,这里给的是最常见的离线版本。

还能怎么玩

  • 把“正方形”改成“高度不同的长方形”,把 size 拆成 width 和 height,查询逻辑不变,只是抬高改为 base + height。
  • 需要同时支持“删除块”?就不能用简单覆盖了,要维护“段的多层高度”,通常换成可并堆或分块/可撤销数据结构,复杂度就上一个台阶。

-END-

我为大家打造了一份RPA教程,完全免费:songshuhezi.com/rpa.html

🔥虎哥私藏精品🔥

虎哥作为一名老码农,整理了全网最全《python高级架构师资料合集》,总量高达650GB,点击下方公众号回复关键字 python 全部免费领