Python技术迷

20K技术经理三面全过,被 HR总监秒杀,沾过仲裁的一律不进。。

刚刷到个贴子,有位技术经理三轮面试全过,月薪20K都谈妥了,结果HR总监一句“打过仲裁的一律不进”,直接秒毙。理由是之前仲裁过。

Image

网友骂得也挺狠,但也有理:企业打赢仲裁叫正义,员工打赢就成“危险分子”?换句话说,不听话的牛就不准进圈吃草。从程序员角度看,这种“用人潜规则”最可怕的不是拒人,而是它默认了你不能有立场。

不过话说回来,这也提醒我们——法律是底线,但职场生存得靠隐形逻辑。别光想着代码优雅,自己简历上那点“历史记录”,也得学会删改缓存。

总的来说,技术值钱是真理,但在大多数公司,乖巧+沉默才是通行证。【备注:文末可领最新资料】

面试题:全 O(1) 的数据结构

刚刚我在地铁上刷知乎,看到一个人在问怎么实现一个支持「全 O(1) 操作」的数据结构……我当时一边听着旁边小孩吵闹,一边突然想到之前被这玩意儿坑过,那叫一个心累。

你想啊,啥叫全 O(1)?就比如 insert、delete、getMax、getMin,全都一毫秒搞定。说白了就是,得让你数据结构既能快速查,又能快速更新,还得知道当前最大和最小的那个值在哪。这不是强迫症工程师的最爱嘛。

我那会儿是凌晨两点半,在公司通宵写脚本,领导第二天早上要演示,非得我加个功能“实时统计热点Key”,我一开始直接用 dict + heap 实现,结果 getMax 操作卡得不行,查堆又慢得一批。

后来我琢磨着用双向链表配哈希表搞一个。你听着别急,这思路其实蛮清楚的。

  • 一个 HashMap 存 key 到它所在链表节点的映射,叫 key_count_map。
  • 然后我们整个链表,每个节点是一个「相同频率」的 key 集合,也就是频率节点,叫 count_node。
  • 然后再用另一个 HashMap 存 count 到 count_node 的映射,这样插入删除的时候能秒到位置。

举个例子你就懂了:假设你现在有 key "a",它出现了 3 次,那它就在 count 为 3 的那个链表节点里;后来你再调用一次 inc("a"),它就得从 count 3 节点挪到 count 4 节点,链表指针调一下就行,O(1)。

来个 Python 写法,你感受下:

classNode:
def__init__(self, count):
        self.count = count
        self.keys = set()
        self.prev = self.next = None

classAllOne:
def__init__(self):
        self.head = Node(float('-inf'))
        self.tail = Node(float('inf'))
        self.head.next = self.tail
        self.tail.prev = self.head
        self.key_map = {}      # key -> Node
        self.count_map = {}    # count -> Node

def_add_node_after(self, new_node, prev_node):
        new_node.prev = prev_node
        new_node.next = prev_node.next
        prev_node.next.prev = new_node
        prev_node.next = new_node

def_remove_node(self, node):
        node.prev.next = node.next
        node.next.prev = node.prev
del self.count_map[node.count]

definc(self, key):
if key notin self.key_map:
            count = 1
            node = self.count_map.get(1)
ifnot node:
                node = Node(1)
                self.count_map[1] = node
                self._add_node_after(node, self.head)
            node.keys.add(key)
            self.key_map[key] = node
else:
            node = self.key_map[key]
            next_count = node.count + 1
            next_node = self.count_map.get(next_count)
ifnot next_node:
                next_node = Node(next_count)
                self.count_map[next_count] = next_node
                self._add_node_after(next_node, node)
            next_node.keys.add(key)
            self.key_map[key] = next_node
            node.keys.remove(key)
ifnot node.keys:
                self._remove_node(node)

defdec(self, key):
if key notin self.key_map:
return
        node = self.key_map[key]
if node.count == 1:
del self.key_map[key]
            node.keys.remove(key)
ifnot node.keys:
                self._remove_node(node)
else:
            prev_count = node.count - 1
            prev_node = self.count_map.get(prev_count)
ifnot prev_node:
                prev_node = Node(prev_count)
                self.count_map[prev_count] = prev_node
                self._add_node_after(prev_node, node.prev)
            prev_node.keys.add(key)
            self.key_map[key] = prev_node
            node.keys.remove(key)
ifnot node.keys:
                self._remove_node(node)

defgetMaxKey(self):
if self.tail.prev == self.head:
return""
return next(iter(self.tail.prev.keys))

defgetMinKey(self):
if self.head.next == self.tail:
return""
return next(iter(self.head.next.keys))

你看它每个操作都不遍历链表、不排序,全是指针跳来跳去的事,所以时间复杂度就稳稳是 O(1)。唯一的麻烦就是链表和哈希的组合操作看起来有点绕,调试的时候我那天连着喝了三罐红牛。

还有一次我用这个写了个 Redis 热 Key 监控的 demo,就挂在服务器上跑,每分钟更新一波 Key 热度排行榜,几乎不吃 CPU,简直感人。

唉行了我啰嗦这么多,你先理解这个结构能干啥,代码也可以自己改改练手。我先去吃点东西,刚刚说到“getMaxKey”我突然想起我外卖还没到……

-END-

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

🔥虎哥私藏精品🔥

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