Python技术迷

33岁后端开发:今年找工作太难了,小厂觉得你待不久,中厂觉得你贵,大厂觉得你老

一个33岁的后端开发出来找工作,投了一圈之后发现,技术栈、项目经验先不聊,年龄和预期薪资已经先被人盘一遍了。

小公司一看你履历,心里嘀咕:这人是不是来了也待不住,给不起成长空间也留不住人。中型公司一算成本,又觉得你要价高,性价比不如招个年轻点的慢慢带。大厂那边更直接,简历扫到33岁,估计就开始怀疑你还有没有那么能熬。

Image

这就很魔幻。你年轻的时候嫌你经验少,等你真干了几年,又嫌你老、嫌你贵、嫌你稳定性不好。打工人像是被卡在一个特别尴尬的位置,往前一步是成本,往后一步是淘汰。

HR看完可能也沉默,程序员看完血压已经上来了。33岁而已,怎么搞得像职场保质期快到了。

算法题:检测正方形

count([11,10]) 返回 1,换一批点返回 0,这题最容易写成“几何题”。

我第一眼不太信它是几何题。

题目叫「检测正方形」,但它检测的不是任意角度的正方形,而是边和 x、y 轴平行的正方形。这一点很关键。只要这个限制在,斜率、向量、两点距离,全都不用上。

题目大概是这样:

不断往系统里加点:

add([x, y])

然后给一个查询点:

count([x, y])

问:用这个查询点,再从已经添加过的点里选 3 个点,可以组成多少个正方形。

注意,点可以重复添加。重复点要按次数算。

这地方我一般不会先存一个点列表然后暴力扫三层。数据一大,基本就挂了。

更舒服的存法是:

(x, y) -> 出现次数

查询时只盯着和查询点在同一条竖线上的点。

比如查询点是 (x, y),如果另一个点是 (x, yy),那它们可以作为正方形的一条竖边。

边长就是:

side = yy - y

只要 side != 0,另外两个点只能在左边或者右边:

右边:

(x + side, y)
(x + side, yy)

左边:

(x - side, y)
(x - side, yy)

这就行了。

代码我会这么写,不搞花活:

from collections import defaultdict


classDetectSquares:

def__init__(self):
        self.point_cnt = defaultdict(int)
        self.same_x = defaultdict(list)

defadd(self, point):
        x, y = point
        key = (x, y)

if self.point_cnt[key] == 0:
            self.same_x[x].append(y)

        self.point_cnt[key] += 1

defcount(self, point):
        x, y = point
        ans = 0

for yy in self.same_x[x]:
if yy == y:
continue

            side = yy - y

# 往右找一个正方形
            right_bottom = (x + side, y)
            right_top = (x + side, yy)
            ans += (
                self.point_cnt[(x, yy)]
                * self.point_cnt[right_bottom]
                * self.point_cnt[right_top]
            )

# 往左找一个正方形
            left_bottom = (x - side, y)
            left_top = (x - side, yy)
            ans += (
                self.point_cnt[(x, yy)]
                * self.point_cnt[left_bottom]
                * self.point_cnt[left_top]
            )

return ans

这里有个细节,same_x[x] 里我只放第一次出现的 y。

为什么?

因为重复点已经在 point_cnt 里记次数了。如果 same_x[x] 也重复塞,查询时会被多算一遍,这种 bug 很隐蔽,样例可能还过。

比如 (3, 10) 被 add 了两次,它应该影响的是组合数量:

self.point_cnt[(3, 10)] == 2

而不是循环两次。

这题真正要盯住的不是“正方形怎么判断”,而是“从查询点反推出另外三个点”。只要确定一条竖边,剩下两个点的位置就是死的。

复杂度也比较稳。

add 基本是 O(1)。

count 和当前查询点同 x 的不同 y 数量有关。实际写题的时候,这比扫所有点舒服太多。

我见过一种写法是枚举所有点,把它当成对角线点,然后去找另外两个点。也能做,但脑子要多转一下,还容易把边长正负写乱。

这题按竖边拆,判断顺序更像查表:

先找同 x 的点
再算边长
再查左右两个角
最后乘次数

写完自己拿一个小例子跑一下:

ds = DetectSquares()
ds.add([3, 10])
ds.add([11, 2])
ds.add([3, 2])

print(ds.count([11, 10]))  # 1

ds.add([11, 2])
print(ds.count([11, 10]))  # 2

第二次为什么是 2?

因为 (11, 2) 加了两次,同一个几何位置,但题目说重复点按次数算。很多人错就错在这里,直接用 set 存点,答案立刻少一截。

这题不用把它想复杂。

点可重复,用计数表。

正方形不旋转,用坐标差。

查询点固定,枚举同一竖线上的点。

剩下就是查三个哈希表位置。做到这里,代码基本就顺了。