← 2026-07-03 📂 All Days 2026-07-07 →

🏗️ 系统设计 / System Design — Day 83 · Expert Synthesis

主题 / Topic: 综合对比 — 当系统撞上系统:设计决策的跨系统矩阵

Synthesis: When Systems Collide — Cross-Architecture Decision Matrix


想象一下 / Real-World Scenario

你是一家独角兽公司的 Staff Engineer。CTO 问你:"我们要同时支持实时聊天、视频流、支付处理,以及搜索自动补全。这四个能共用同一套基础设施吗?"

You're a Staff Engineer at a unicorn. CTO asks: "We need real-time chat, video streaming, payment processing, and search autocomplete simultaneously. Can they share infrastructure?"

答案不是"能"或"不能"—— 而是"哪些层可以共享,哪些必须隔离"。

The answer isn't yes or no — it's "which layers can share, which must isolate."


四大系统的核心约束对比 / Core Constraints Comparison

System | Latency | Throughput | Consistency | Storage | Protocol ---------------|----------|------------|-------------|-------------|---------- Chat (Slack) | <100ms | Medium | Eventual OK | Hot+Archive | WebSocket Video (Netflix)| <200ms | Very High | Eventual OK | CDN-heavy | HLS/DASH Payment | <500ms | Low-Med | STRONG ⚠️ | ACID DB | HTTPS/REST Autocomplete | <50ms | Very High | Stale OK | In-Memory | HTTP/REST ↑ Payment is the odd one out: it CANNOT share eventual-consistency infra

架构决策矩阵 / What Can Be Shared

┌────────────────────────────────────┐ │ Shared Layer │ │ ┌──────────┐ ┌────────────────┐ │ │ │ API │ │ Auth/AuthZ │ │ │ │ Gateway │ │ (JWT, OAuth) │ │ │ └──────────┘ └────────────────┘ │ │ ┌──────────┐ ┌────────────────┐ │ │ │ CDN/Edge │ │ Observability │ │ │ │ (static) │ │ (logs/traces) │ │ │ └──────────┘ └────────────────┘ │ └────────────────────────────────────┘ │ ┌────────────────────────────┼──────────────────────────┐ ▼ ▼ ▼ ┌─────────────┐ ┌──────────────┐ ┌──────────────┐ │ Chat + Video│ │ Payment │ │ Autocomplete │ │ (eventual) │ │ (isolated!) │ │ (read-only) │ │ Kafka+Redis │ │ Postgres+2PC │ │ Redis/Trie │ └─────────────┘ └──────────────┘ └──────────────┘

✅ 可以共享 / Can Share:

  • API Gateway (rate limiting, routing)
  • CDN (static assets)
  • Authentication service
  • Distributed tracing (Jaeger/OpenTelemetry)
  • Object storage (S3) for artifacts

❌ 不能混用 / Must Isolate:

  • Payment ← MUST have its own ACID database, cannot share eventual-consistency Kafka pipeline
  • Autocomplete ← Must be read-optimized; never co-locate with write-heavy workloads
  • Video transcoding ← CPU-bound; isolate in separate worker pools

关键权衡 / Key Tradeoffs

1. 一致性隔离 / Consistency Isolation

Chat 可以接受"消息延迟几秒"(最终一致性),但 Payment 必须保证"扣款只发生一次"(强一致性)。把它们放在同一个数据库里会让 Chat 的性能拖慢 Payment,或让 Payment 的锁机制拖慢 Chat。

Chat can tolerate "message delayed by seconds" (eventual consistency), but Payment demands "charge happens exactly once" (strong consistency). Mixing them either kills Chat performance via Payment's locking, or compromises Payment's correctness.

2. 流量模式 / Traffic Pattern Mismatch

  • Autocomplete: 每秒百万次读取,极低写入 → Redis sorted sets, in-memory only
  • Video: 大块字节流 → 不走应用服务器,直接 CDN-to-client
  • Chat: 小包高频 → WebSocket long-lived connections

3. 故障域隔离 / Blast Radius

❌ Bad: Payment service shares DB connection pool with Chat → Chat traffic spike exhausts connections → Payment fails ✅ Good: Bulkhead pattern — each system has independent connection pools → Chat DB slow → only Chat degrades; Payment unaffected

综合架构原则 / Synthesis Principles (Staff-Level)

  1. 按一致性需求划分数据边界 — 强一致性系统永远隔离

Partition data boundaries by consistency requirement — strong-consistency systems always isolated

  1. 共享无状态层,隔离有状态层 — API Gateway/Auth 可共享;数据库不行

Share stateless layers, isolate stateful ones — API Gateway/Auth can share; databases cannot

  1. 识别"写放大"风险 — 视频上传触发转码队列,转码队列不能与支付队列共用 worker

Identify write amplification risks — video upload → transcode queue must not share workers with payment queue

  1. 降级策略需匹配 — 聊天系统降级 = 消息缓冲几秒(可接受);支付系统降级 = 拒绝请求(不能丢单)

Degradation strategies must match business requirements — chat buffering is OK; payment must reject, never silently lose


别踩这个坑 / Common Mistakes

❌ 把 Kafka 当"万能总线"接入支付流程
   Using Kafka as universal bus for payment — at-least-once delivery ≠ exactly-once billing

❌ 让搜索索引和业务数据库共用同一个 Postgres
   Sharing Postgres between search index and business data — vacuum/autovacuum conflicts

❌ 跨系统共享缓存 key 空间 (Redis)
   Sharing Redis key namespace across systems — cache eviction policies conflict

✅ 正确做法: 每个系统独立的 Redis instance 或 cluster,通过 prefix + TTL policy 隔离
   Each system gets its own Redis cluster/namespace with independent eviction policy

📚 References


🧒 ELI5

想象四家餐厅共用一栋大楼:

  • 快餐店(聊天):效率第一,偶尔上错菜没关系
  • 电影院小卖部(视频):卖大爆米花桶,走量
  • 银行柜台(支付):每一笔账必须准确,绝不能错
  • 糖果店(搜索):只卖不进货,货架永远满着

快餐店和电影院可以共用仓库,但银行柜台绝对不能跟快餐店共用收银机。

Imagine four restaurants in one building. The cash register at the bank counter can NEVER be shared with the fast-food counter — even if it looks like the same hardware. Isolation is about trust and correctness, not just hardware cost.

💻 算法 / Algorithms — Day 83 · #973 K Closest Points to Origin (Medium)

🧩 堆/优先队列模式 (3/7) — building on the template from Day 79 (Kth Largest in Stream) and Day 81 (Last Stone Weight)


题目 / Problem

🔗 LeetCode #973 🟡 Medium

📹 NeetCode Solution

今天的变化 / What's different from previous problems in this block:

  • Day 79 (Kth Largest in Stream): 1D stream, max-heap with size-k filter
  • Day 81 (Last Stone Weight): simulation, always pop 2 max elements
  • Today: 2D distance comparison, need to return K points (not just count/value)

关键变化: 堆的 key 不再是值本身,而是距离 — 且需要存储原始点以便返回结果。

Key shift: heap key is now a derived metric (distance), not the raw value, and we must store the original point.


现实类比 / Real-World Analogy

你在做外卖 APP。用户要找最近的 K 家餐厅。暴力方法:把全城餐厅按距离排序。聪明方法:维护一个大小为 K 的"候选最近列表",遍历餐厅时,如果有更近的就换掉最远的那家。

You're building a delivery app. Find the K nearest restaurants. Brute force: sort all restaurants by distance. Smart: maintain a "top-K nearest" heap, swap out the farthest whenever you find something closer.


模版映射 / Mapping to the Pattern Template

# 通用模版 / General Template (from Day 79):
heap = []
for item in stream:
    heapq.heappush(heap, item)
    if len(heap) > k:
        heapq.heappop(heap)

# 今天的应用 / Today's Application:
# item → (-distance, x, y)  ← 取负值让 heapq (min-heap) 行为像 max-heap
# 维护大小为k的"最大距离堆" → pop掉最远的 → 留下最近的k个

解法 / Solution with Trace

import heapq
from typing import List

def kClosest(points: List[List[int]], k: int) -> List[List[int]]:
    # Max-heap trick: negate distance so Python's min-heap pops the FARTHEST point
    heap = []  # stores (-dist_squared, x, y)
    
    for x, y in points:
        dist = x*x + y*y  # skip sqrt — monotonic, doesn't affect ordering
        heapq.heappush(heap, (-dist, x, y))
        if len(heap) > k:
            heapq.heappop(heap)  # remove the farthest point
    
    return [[x, y] for _, x, y in heap]

逐步追踪 / Trace — points = [[1,3],[-2,2],[3,1],[0,1]], k = 2

Point [1,3]: dist=10, heap=[(-10, 1, 3)] size=1 ≤ k, no pop Point [-2,2]: dist=8, heap=[(-10,1,3),(-8,-2,2)] size=2 ≤ k, no pop Point [3,1]: dist=10, heap adds (-10,3,1) → size=3 > k pop farthest: (-10,1,3) or (-10,3,1) [tied] → one removed heap=[(-10,3,1),(-8,-2,2)] ← still has dist=10 point! Point [0,1]: dist=1, heap adds (-1,0,1) → size=3 > k pop farthest: (-10,3,1) removed heap=[(-8,-2,2),(-1,0,1)] Result: [[-2,2],[0,1]] ✓

复杂度 / Complexity:

  • Time: O(n log k) — n points, each heap op is O(log k)
  • Space: O(k) — heap stores at most k+1 points

为什么不用排序?/ Why Not Just Sort?

# ❌ 暴力排序 / Brute force sort
points.sort(key=lambda p: p[0]**2 + p[1]**2)
return points[:k]  # O(n log n) — slower for large n, small k

# ✅ 堆方法 / Heap approach  
# O(n log k) — much faster when k << n
# Example: n=1,000,000, k=10
# Sort: 1M * 20 = 20M ops
# Heap: 1M * log(10) ≈ 3.3M ops  ← 6x faster

举一反三 / Connect to Pattern Block

Block so far: #703 Kth Largest in Stream → size-k min-heap, return top element #1046 Last Stone Weight → always pop 2 max, simulate #973 K Closest Points ← → size-k max-heap (negated), return all k elements Coming next: #215 Kth Largest in Array → same as #703 but not streaming — can use QuickSelect O(n) average! #621 Task Scheduler → heap for scheduling, not just finding — frequency counting #355 Design Twitter → heap merge (K sorted streams) #295 Find Median → TWO heaps (max-heap + min-heap)

进阶挑战 / Pro Challenge: Can you solve this with QuickSelect in O(n) average? (Hint: partition around k-th distance)


📚 References


🧒 ELI5

你有一堆弹珠,要找离你最近的3颗。

方法1:把所有弹珠按距离排好序,取前3个(慢)。

方法2:拿一个只能装3颗的小盒子。遍历每颗弹珠,如果比盒子里最远的还近,就换进去。

最后盒子里剩的就是答案!堆就是那个"智能小盒子"。

You have a bag of marbles. Find the 3 closest to you. Method 2: keep a box that holds only 3 marbles. If you find one closer than the farthest in the box, swap it in. The heap IS that smart box.


🗣️ 软技能 / Soft Skills — Day 83 · Expert Synthesis

主题 / Topic: 综合场景 — 当技术债、团队文化、商业压力同时撞上你

Synthesis Scenario: When Tech Debt, Team Culture, and Business Pressure Collide Simultaneously


为什么这个场景最难 / Why This Is the Hardest Scenario

在 Staff+ 层面,最难的不是单个挑战——而是三件事同时发生:

  • 线上系统有技术债需要还(工程判断)
  • 团队士气因为一直"填坑"在下滑(人的问题)
  • 新 feature 业务方催得很急(商业压力)

At Staff+ level, the hardest challenges aren't singular — they're compound:

  • Legacy tech debt threatening reliability (engineering judgment)
  • Team morale declining from perpetual firefighting (people problem)
  • Business stakeholders demanding new features now (commercial pressure)

面试官真正在测什么: 你能否在不消耗团队信用的情况下,同时推进这三条线?


STAR 分解 / STAR Framework

Situation (背景)

"在我的上一个团队,我们有一个 5 年历史的单体 Python 服务。它支撑了公司 40% 的营收,但部署一次需要 45 分钟,P99 延迟在大促期间会飙到 8 秒。与此同时,产品路线图上有 6 个新特性等着交付,团队的工程师开始在 1:1 里提到'感觉一直在擦屁股'。"

"My previous team owned a 5-year-old Python monolith supporting 40% of company revenue. Each deploy took 45 minutes, P99 spiked to 8s during peak. Meanwhile, 6 new features sat on the roadmap, and engineers in 1:1s were saying 'we're always firefighting.'"

Task (任务)

同时解决三个问题:降低技术债风险、修复团队士气、交付业务需求。

Simultaneously: reduce tech debt risk, fix team morale, deliver business features.

Action (行动)

第一步:量化,不要定性 / Quantify, don't moralize

❌ "我们的技术债很严重,需要重构"  (谁来判断?)
✅ "每个新特性因为历史包袱多花3周,上季度我们损失了9周 = 2.25个工程师月"
   "Each new feature costs 3 extra weeks. Last quarter: 9 wasted weeks = 2.25 eng-months lost"

第二步:捆绑利益,不要单独提案 / Bundle interests, don't silo proposals

  • 告诉 PM:重构 = 下个季度特性速度提升 2x
  • 告诉 CTO:不重构的风险 = 大促崩溃,损失 $2M ARR
  • 告诉团队:重构计划 = 你们主导,不是我指挥

第三步:20% 时间规则 / 20% refactor rule

每个 Sprint 划出 20% 的时间(约 1 天/工程师/周)专门用于还技术债。这个预算不需要业务审批——它是工程团队的"运营开销"。

Reserve 20% of each Sprint for debt reduction. This budget doesn't require business approval — it's engineering "operating cost."

第四步:可见性 / Make progress visible

建一个"技术健康仪表盘"(部署时间、P99、代码覆盖率),每周 standup 一分钟汇报。团队能看到自己的工作成果 → 士气回升。

Build a "tech health dashboard" (deploy time, P99, test coverage). One-minute standup update weekly. Engineers see impact of their work → morale recovers.

Result (结果)

6个月后:

  • 部署时间:45分钟 → 8分钟
  • P99 大促峰值:8秒 → 1.2秒
  • 特性交付速度:提升 1.8x
  • 2 名工程师提到这是他们"最好的6个月工作体验"

❌ 弱回答 vs ✅ 强回答

❌ 弱: "我们做了一个技术债冲刺,然后就好了"

  • 没有量化
  • 没有展示跨职能对齐
  • 没有可持续机制

✅ 强: 展示系统性思维 — 不是"修一个问题",而是"改变了技术债如何被优先级排序的游戏规则"

❌ Weak: "We did a tech debt sprint and fixed it."

✅ Strong: Show systemic thinking — you changed HOW tech debt gets prioritized, not just addressed one instance.


Senior/Staff 加分项 / Senior+ Differentiators

  1. 你主动发现了这个问题,而不是被指派去解决它 — Proactive pattern recognition
  2. 你创造了可持续的机制(20% 规则),而不是一次性冲刺 — Durable systems beat one-time heroics
  3. 你会说服 PM 和 CTO,不只是工程团队 — Cross-functional influence
  4. 你把工程健康变成了可量化的业务指标 — Translation between eng and business language

关键要点 / Key Takeaways

  • 技术债故事的 Staff 级别框架:量化损失 → 捆绑业务利益 → 建立可持续机制 → 公开进度
  • 最强的 STAR 故事不是"我解决了一个难题",而是"我改变了问题被处理的方式"
  • The strongest STAR stories aren't "I solved a hard problem" — they're "I changed how the problem gets handled"

📚 References


🧒 ELI5

你家的水管漏水但还能用,同时老板让你装修新卧室,同时你的家人说"每天都在修水管太烦了"。

聪明做法:告诉老板"修水管能让装修快2倍",每周抽1天专门修管道,贴进度条让全家人都能看到。三件事同时推进,而不是硬选一个。

Your pipes are leaking but functional, your boss wants a new bedroom, and your family says "we're always fixing pipes." Smart move: tell the boss fixing pipes makes remodeling 2x faster, dedicate 1 day/week to pipes, post a progress board everyone can see. Advance all three simultaneously.

🤖 AI — Day 83 · 最新动态 / AI News Roundup (July 2026)

📰 NEWS MODE — sourced from web search conducted July 6, 2026

本周 AI 三大新闻 / Top 3 AI Stories This Week


1. 🚀 OpenAI 预览 GPT-5.6 家族,但访问受限

OpenAI Previews GPT-5.6 Family, Access Restricted to ~20 Partners

OpenAI 预览了 GPT-5.6 系列,包含三个层次:Sol(旗舰)、Terra(平衡)、Luna(快速经济)。据报道,目前访问仅限约 20 家政府审查的合作伙伴,更广泛的开放预计在 7 月中下旬。

OpenAI previewed the GPT-5.6 family with three tiers: Sol (flagship), Terra (balanced), and Luna (fast/affordable). Access is reportedly limited to ~20 government-vetted partner organizations, with broader availability expected mid-to-late July.

值得注意 / Notable: OpenAI 计划将 GPT-5.6 Sol 部署在 Cerebras 基础设施上,据报道可达到约 750 token/秒的生成速度 — 比标准部署快 5-10 倍。

OpenAI plans to deploy GPT-5.6 Sol on Cerebras infrastructure, reportedly enabling ~750 tokens/second — 5-10x faster than standard deployments.

📎 来源 / Source: TLT AI Brief July 2026


2. ⚖️ Anthropic Claude Sonnet 5 成为新默认,监管阴云犹在

Anthropic Launches Claude Sonnet 5 as New Default; Regulatory Cloud Lingers

Anthropic 发布了 Claude Sonnet 5,成为所有 Free 和 Pro 用户的新默认模型。与此同时,更早的 Claude Fable 5 和 Mythos 5 因美国出口管控指令(发现了能够识别软件漏洞的越狱方式)而遭到临时暂停,目前部分恢复,但 Mythos 5 仍限于经批准的美国机构。

Anthropic launched Claude Sonnet 5 as the new default for Free and Pro users. Earlier, Claude Fable 5 and Mythos 5 faced temporary suspension under a U.S. export control directive after a jailbreak demonstrated the models' ability to identify software vulnerabilities. Access was partially restored, with Mythos 5 still restricted to approved U.S. organizations.

工程师视角 / Engineer's Take: 这标志着政府直接干预 AI 模型发布的先例 — 不只是事后监管,而是预防性限制。对于依赖 API 的产品,需要关注模型访问的不确定性风险。

This sets a precedent for government preemptive restrictions on model releases — not just post-hoc regulation. Product teams relying on API access need to plan for availability uncertainty.

📎 来源 / Source: AI Central Substack — The AI Landscape July 2026


3. 📍 Illinois 签署 AI 安全法,美国 AI 立法进入多州竞赛

Illinois Signs AI Safety Act; U.S. State-Level AI Regulation Accelerates

伊利诺伊州州长 JB Pritzker 签署《人工智能安全措施法》,建立针对能够造成"灾难性伤害"的大型 AI 模型的护栏和报告标准。这与加州、纽约的类似立法呼应,标志着美国 AI 监管从联邦缺位转向州级竞赛。

Illinois Governor JB Pritzker signed the Artificial Intelligence Safety Measures Act, establishing guardrails and reporting standards for large AI models capable of causing "catastrophic harm." This mirrors similar legislation in California and New York — signaling that U.S. AI regulation is shifting from federal inaction to state-level competition.

实际影响 / Practical Impact: AI 公司可能面临各州不一致的合规要求 — 类似 GDPR 在欧洲落地时给工程团队带来的挑战(地理路由、数据本地化、审计日志)。

AI companies may face inconsistent state-level compliance requirements — similar to how GDPR fragmented engineering practices (geo-routing, data localization, audit logs).

📎 来源 / Source: WTTW News — Pritzker signs AI regulation bill


📊 本周 AI 格局速览 / Quick Landscape Snapshot

Provider | Latest Model | Context | Notable ------------|-------------------|---------|--------------------------- OpenAI | GPT-5.6 Sol | TBD | 750 tok/s on Cerebras (limited) Anthropic | Claude Sonnet 5 | 200K | New default; Mythos 5 restricted Google | Gemini 3.1 Pro | 1M | Cost-efficient reasoning Open Source | GLM-5.2 | - | Freely downloadable, rival proprietary

工程师必知 / Engineer's Takeaway

速度军备竞赛: 750 tok/s 不只是炫耀数字 — 它让实时 AI 应用(实时字幕、代码补全、语音 AI)成为可能。当前 GPT-4 约 30-50 tok/s,差距约 15-25 倍。

合规新现实: 如果你在美国构建 AI 产品,需要开始跟踪各州 AI 安全法。2026 年的格局:加州 + 纽约 + 伊利诺伊已有立法,更多州在路上。

Speed arms race: 750 tok/s enables real-time AI applications impossible at current speeds.

Compliance reality: U.S.-based AI products now need state-by-state compliance tracking.


📚 References


🧒 ELI5

OpenAI 做了一个超快的新 AI,但只给少数人用(像是新电影只在部分影院放映)。

Anthropic 的新模型面向所有人,但老模型因为"会找安全漏洞"被政府叫停了一段时间。

伊利诺伊州通过了法律,要求危险 AI 系统必须登记报告 — 就像汽车需要年检一样。

OpenAI made a super-fast new AI but only a few people can use it. Anthropic's new model is for everyone, but their older model got temporarily banned because it could find security holes. Illinois passed a law requiring dangerous AI systems to report themselves — like cars needing a safety inspection.

🔬 Saturday Deep Dive: 堆 / 优先队列 — 从 K 个最近点到数据流中位数 (18 min read)

📊 NeetCode: 65/150 · SysDesign: 64/40 · Behavioral: 64/40 · Frontend: 37/50 · AI: 30/30

🔥 持续精进中!


Overview / 概述

为什么堆很重要?

堆(优先队列)是面试中频率极高的数据结构——只要题目出现 "top K"、"K 个最小/最大"、"合并 K 个有序序列"、"动态中位数" 这类关键词,第一反应就应该是堆。

核心价值:不需要对整个数组排序(O(n log n)),只维护一个大小为 k 的堆(O(n log k))。当 k << n 时,性能差异巨大。

本期深度讲解三个层次:

  1. 基础:K 个最近点(中等,热身)
  2. 进阶:数组第 K 大元素(中等,多种解法对比)
  3. 巅峰:数据流中位数(困难,双堆经典)

Why heaps matter:

The heap (priority queue) pattern shows up whenever you need to efficiently maintain the "top K" of a dynamic dataset. The insight is powerful: instead of re-sorting everything, you maintain a bounded heap that does O(log k) work per element instead of O(log n). For "find median from a stream" this becomes the two-heap trick — one of the most elegant data structure designs in interview canon.

🔗 K Closest Points to Origin — LeetCode #973

🔗 Find Median from Data Stream — LeetCode #295

📹 NeetCode — K Closest Points

📹 NeetCode — Find Median from Data Stream


Part 1: Theory / 理论基础 (5 min)

堆的本质 / The Heap Mental Model

Python 的 heapq 是最小堆:最小值永远在堆顶(index 0)。

最小堆示例:
        1
       / \
      3   2
     / \
    4   5

关键技巧:用最小堆模拟最大堆 → 存入负值

import heapq

# Min-heap (default)
min_heap = [3, 1, 4, 1, 5]
heapq.heapify(min_heap)
print(heapq.heappop(min_heap))  # 1

# Max-heap trick: negate values
max_heap = [-3, -1, -4, -1, -5]
heapq.heapify(max_heap)
print(-heapq.heappop(max_heap))  # 5

三种堆的使用模式 / Three Heap Patterns:

场景用什么堆核心操作
Top K 最大值大小为 k 的最小堆若新元素 > 堆顶 → 替换堆顶
Top K 最小值大小为 k 的最大堆若新元素 < 堆顶 → 替换堆顶
动态中位数两个堆(大小各半)平衡两堆大小

为什么维护 K 个最大值要用最小堆?/ Why use a min-heap for top-K largest?

想象你有一个"门卫"在守住前 K 名。新来一个候选人,需要问:"你比现有 K 人里最弱的那个强吗?" 最小堆的堆顶就是那个"最弱"的人——O(1) 时间就能检查,O(log k) 时间踢掉并加入新人。

The min-heap's root is always the weakest element in your top-K set. It's your bouncer. Every new element only gets in if it beats the current weakest. This is the core insight for all "Top K" problems.


Part 2: Step-by-Step Implementation / 一步一步实现 (8 min)

2.1 K Closest Points to Origin (Medium)

问题:给定 n 个点,返回距离原点最近的 k 个点。距离 = √(x² + y²)(比较时可省略 √)。

方法一:直接排序(简单但非最优)

def kClosest_sort(points: list[list[int]], k: int) -> list[list[int]]:
    # Sort all points by squared distance, take first k
    # Time: O(n log n), Space: O(n)
    points.sort(key=lambda p: p[0]**2 + p[1]**2)
    return points[:k]

方法二:最大堆,维护 K 个最小值(面试首选)

import heapq

def kClosest(points: list[list[int]], k: int) -> list[list[int]]:
    # Use a max-heap of size k
    # Python only has min-heap, so negate distance to simulate max-heap
    # Time: O(n log k), Space: O(k)
    
    max_heap = []  # stores (-distance_squared, x, y)
    
    for x, y in points:
        dist_sq = x*x + y*y
        # Push negative distance (max-heap trick)
        heapq.heappush(max_heap, (-dist_sq, x, y))
        
        # If heap exceeds size k, remove the farthest point (heap root = largest dist)
        if len(max_heap) > k:
            heapq.heappop(max_heap)  # removes the most negative = largest distance
    
    # Extract points (discard distance)
    return [[x, y] for (_, x, y) in max_heap]


# Trace through example: points = [[1,3],[-2,2],[5,-1]], k = 2
# 
# Process [1,3]: dist=10  → heap=[(-10, 1, 3)]           size=1 ≤ k, no pop
# Process [-2,2]: dist=8  → heap=[(-10,1,3),(-8,-2,2)]   size=2 ≤ k, no pop
# Process [5,-1]: dist=26 → heap=[(-26,5,-1),(-8,-2,2),(-10,1,3)]  size=3 > k
#   pop max → removes (-26, 5, -1)  [farthest point]
#   heap=[(-10,1,3),(-8,-2,2)]
# 
# Result: [[1,3],[-2,2]] ✓

方法三:Quick Select(最优平均,O(n) 平均)

import random

def kClosest_quickselect(points: list[list[int]], k: int) -> list[list[int]]:
    # Partition-based selection — O(n) average, O(n²) worst
    # Not recommended in interviews unless specifically asked
    
    def dist(p):
        return p[0]**2 + p[1]**2
    
    def partition(left, right, pivot_idx):
        pivot_dist = dist(points[pivot_idx])
        # Move pivot to end
        points[pivot_idx], points[right] = points[right], points[pivot_idx]
        store = left
        for i in range(left, right):
            if dist(points[i]) <= pivot_dist:
                points[store], points[i] = points[i], points[store]
                store += 1
        # Move pivot to final position
        points[store], points[right] = points[right], points[store]
        return store
    
    left, right = 0, len(points) - 1
    while left < right:
        pivot_idx = random.randint(left, right)
        pivot_final = partition(left, right, pivot_idx)
        if pivot_final == k:
            break
        elif pivot_final < k:
            left = pivot_final + 1
        else:
            right = pivot_final - 1
    
    return points[:k]

解法对比 / Comparison:

解法时间复杂度空间复杂度何时用
排序O(n log n)O(1)k 接近 n 时
最大堆O(n log k)O(k)面试首选
Quick SelectO(n) 平均O(1)追求最优、可接受最坏情况

2.2 Find Median from Data Stream (Hard) — 双堆经典

问题:设计一个数据结构,支持:

  • addNum(num): 加入一个整数
  • findMedian(): 返回所有已加入整数的中位数

核心思路 / Core Insight:

把数据分成两半:

  • 左半部分 → 最大堆(堆顶 = 左半最大值)
  • 右半部分 → 最小堆(堆顶 = 右半最小值)

中位数 = 两堆堆顶的均值(或较大堆的堆顶)

数据: [1, 2, 3, 4, 5] 左半 (max-heap): [1, 2] → 堆顶 = 2 右半 (min-heap): [3, 4, 5] → 堆顶 = 3 中位数 = (2 + 3) / 2 = 2.5 ✓ 2 | 3 1 | 4 | 5 max-heap | min-heap
import heapq

class MedianFinder:
    """
    Two-heap approach for dynamic median.
    
    Invariant:
      1. max_heap (left) stores the smaller half — as negatives (min-heap trick)
      2. min_heap (right) stores the larger half
      3. len(max_heap) == len(min_heap)  OR  len(max_heap) == len(min_heap) + 1
         (max_heap can have at most 1 extra element)
    
    Time: O(log n) per addNum, O(1) per findMedian
    Space: O(n)
    """
    
    def __init__(self):
        self.max_heap = []  # left half — stored as negatives
        self.min_heap = []  # right half
    
    def addNum(self, num: int) -> None:
        # Step 1: Always push to max_heap first
        heapq.heappush(self.max_heap, -num)
        
        # Step 2: Balance — ensure max_heap top ≤ min_heap top
        # If max_heap's largest > min_heap's smallest, move it over
        if self.min_heap and (-self.max_heap[0]) > self.min_heap[0]:
            val = -heapq.heappop(self.max_heap)
            heapq.heappush(self.min_heap, val)
        
        # Step 3: Rebalance sizes — max_heap can have at most 1 extra
        if len(self.max_heap) > len(self.min_heap) + 1:
            val = -heapq.heappop(self.max_heap)
            heapq.heappush(self.min_heap, val)
        elif len(self.min_heap) > len(self.max_heap):
            val = heapq.heappop(self.min_heap)
            heapq.heappush(self.max_heap, -val)
    
    def findMedian(self) -> float:
        # If odd total: max_heap has the extra element (middle value)
        if len(self.max_heap) > len(self.min_heap):
            return float(-self.max_heap[0])
        # If even total: average of both tops
        return (-self.max_heap[0] + self.min_heap[0]) / 2.0


# ── Trace through: addNum(1), addNum(2), addNum(3) ──
#
# addNum(1):
#   push -1 to max_heap → max_heap=[-1], min_heap=[]
#   no balance needed (min_heap empty)
#   sizes: 1 vs 0 → ok (max_heap can have +1)
#   state: max_heap=[-1], min_heap=[]
#
# addNum(2):
#   push -2 to max_heap → max_heap=[-2,-1], min_heap=[]
#   balance check: -(-2)=2 > nothing (min_heap empty) → skip
#   size check: len(max_heap)=2 > len(min_heap)+1=1 → move top of max to min
#     pop -2 from max_heap → push 2 to min_heap
#   state: max_heap=[-1], min_heap=[2]
#
# addNum(3):
#   push -3 to max_heap → max_heap=[-3,-1], min_heap=[2]
#   balance check: -(-3)=3 > min_heap[0]=2 → move 3 to min_heap
#     pop -3 from max_heap → push 3 to min_heap
#   max_heap=[-1], min_heap=[2,3]
#   size check: len(min_heap)=2 > len(max_heap)=1 → move min top to max
#     pop 2 from min_heap → push -2 to max_heap
#   state: max_heap=[-2,-1], min_heap=[3]  → wait sizes 2 vs 1, max_heap has +1 ✓
#
# findMedian():
#   len(max_heap)=2 > len(min_heap)=1 → return -max_heap[0] = -(-2) = 2 ✓
#   Actual data: [1,2,3] → median = 2 ✓

为什么这样设计不会出错?/ Why does this invariant work?

关键是两个堆维护了一个"有序分割线":

  • max_heap 里所有元素 ≤ min_heap 里所有元素(Step 2 保证)
  • 两堆大小差最多为 1(Step 3 保证)
  • 因此中位数始终在堆顶,O(1) 可读

The invariant guarantees that after every addNum, the heaps are sorted relative to each other AND balanced in size. The median is always at one or both heap tops — no scanning needed.


Part 3: Edge Cases & Gotchas / 边界情况 (2 min)

K Closest Points:

# Edge case 1: k == len(points) → return all points
# Edge case 2: points on same distance → any k of them valid
# Edge case 3: k == 1 → heap of size 1 = just track current minimum
# GOTCHA: Don't sqrt! x*x + y*y is sufficient for comparison
# GOTCHA: Tuple comparison — heapq compares tuples element by element
#   (-dist, x, y) works because dist disambiguates before x, y are checked

Find Median from Data Stream:

# Edge case 1: single element → return float(that element)
# Edge case 2: two elements → return their average
# Edge case 3: all same values → median is that value

# GOTCHA 1: Don't forget the negation when extracting from max_heap
#   return -self.max_heap[0]  ← the minus sign is easy to forget!

# GOTCHA 2: The balance condition order matters
#   Always push to max_heap FIRST, then check cross-heap ordering,
#   THEN balance sizes. Reversing steps leads to subtle bugs.

# GOTCHA 3: Python's heapq doesn't support decrease-key
#   If you need to remove arbitrary elements, use a lazy deletion pattern
#   or use sortedcontainers.SortedList instead (O(log n) insert+delete)

通用堆陷阱 / General heap gotchas:

# heapq.heappush / heappop work on lists in-place
# heapq does NOT sort the list visually — internal array != sorted order!
import heapq
h = [3, 1, 4, 1, 5, 9]
heapq.heapify(h)
print(h)  # [1, 1, 4, 3, 5, 9] — NOT [1,1,3,4,5,9]!
# But heappop() always returns the minimum correctly

Part 4: Real-World Application / 实际应用 (2 min)

K Closest Points → 地理空间查询 / Geospatial Queries

场景:Uber 司机派单系统
- 每秒有数千个乘客发出打车请求
- 需要找到距离每个乘客最近的 K 个空闲司机
- 直接暴力排序所有司机:O(n log n) 每次请求
- 堆方案:O(n log k),当 k=5 时快 100x

实际实现通常结合地理索引(geohash/quadtree)先缩小候选集,
再用堆做最终 K 个筛选。

Find Median from Data Stream → 实时监控 / Real-time Monitoring

场景:Datadog / Prometheus 实时 P50 延迟
- 服务每秒产生数千次 API 调用延迟数据
- 需要实时展示 P50(中位数)延迟
- 不能存所有历史数据再排序(内存有限)
- 双堆方案:O(log n) 插入,O(1) 读取中位数
- 类似地可以用 T-Digest 或 HDRHistogram 做近似中位数

相关拓展:
- Redis ZSET:内部用跳表,支持 O(log n) rank 查询
- Apache Kafka Streams:内置滑动窗口统计

Heap 在生产系统中的常见用途 / Heap uses in production:

系统堆的用途
OS 任务调度优先级队列调度进程
Dijkstra 最短路最小堆取当前最近节点
Huffman 编码合并最小频率节点
Top-K 日志分析维护 K 个最高频错误
推荐系统维护用户 Top-K 候选集

Part 5: Interview Simulation / 面试模拟 (3 min)

Q1: K Closest Points 时间复杂度是多少?什么时候用排序更好?

堆方案 O(n log k),排序方案 O(n log n)。当 k 很大(k ≈ n)时,排序不一定更慢,因为常数因子更小;当 k << n(比如 k=10, n=1000000)时,堆方案快很多。面试时两种都说,然后给 tradeoff。

Q2: Find Median 如果允许 O(n) 空间但要 O(1) addNum,能做到吗?

不能在最坏情况下保证 O(1) addNum + O(1) median。堆方案已经是最优了:O(log n) addNum,O(1) median。如果只需要近似中位数,可以用 T-Digest(流式统计算法),但精确中位数无法比 O(log n) 更快。

Q3: 如何扩展双堆方案支持删除某个特定数字?

标准堆不支持 O(log n) 随机删除。解法:懒删除(Lazy Deletion)——用一个 Counter 记录"已删除但还在堆里"的元素,heappop 时检查并跳过。时间复杂度均摊 O(log n)。或者用 SortedList(sortedcontainers 库),O(log n) 支持任意插入删除。

Q4: 数组第 K 大(LeetCode #215)有更快的解法吗?

Quick Select 平均 O(n),最坏 O(n²)。堆方案 O(n log k)。对于固定 k 值,堆通常更可预测(无最坏情况)。面试时如果面试官追问"能做到线性吗",则答 Quick Select。
def findKthLargest(nums: list[int], k: int) -> int:
    # Min-heap of size k — top is the kth largest
    # Time: O(n log k), Space: O(k)
    heap = []
    for n in nums:
        heapq.heappush(heap, n)
        if len(heap) > k:
            heapq.heappop(heap)
    return heap[0]  # kth largest is the smallest in our top-k heap

Q5: 如果数据流数据量超大(PB 级),如何设计分布式中位数?

单机双堆不够用时:
1. 采样方案:随机采样 + 排序,给近似中位数(Reservoir Sampling)
2. 分段汇总:每台机器维护本地直方图(histogram),汇总时合并直方图找中位数(Spark/Flink 的实现思路)
3. T-Digest:Databricks/Spark 用的流式近似中位数算法,空间 O(1/epsilon²),精度可配置

🗺️ 本模式剩余问题 / Remaining Pattern Problems

Heap / Priority Queue 完整路径 (共 7 题): ✅ #703 Kth Largest Element in a Stream (Easy) ← Day 79 ✅ #1046 Last Stone Weight (Easy) ← Day 81 ✅ #973 K Closest Points to Origin (Medium) ← Today (Day 82) ⬜ #215 Kth Largest Element in an Array (Medium) ← Next ⬜ #621 Task Scheduler (Medium) ⬜ #355 Design Twitter (Medium) ⬜ #295 Find Median from Data Stream (Hard) ← 本期深度解析

byte-by-byte · Day 82 · Saturday Deep Dive

🔗 K Closest Points · Find Median from Data Stream

📹 NeetCode K Closest · NeetCode Median

📅 Week in Review — Week 28 (10 min read)

📊 NeetCode: 67/150 · SysDesign: 64/40 · Behavioral: 64/40 · Frontend: 37/50 · AI: 30/30

🔥 坚持精进,复合成长!


🗓️ 本周回顾 / This Week's Journey

日期 / Date主题摘要 / Summary
周一 Jun 30 (Day 78)AI 聊天平台深度拆解;Word Search II(Trie + DFS 终极 Boss);连接池实战;AGI 与新兴架构
周二 Jul 1 (Day 79)ChatGPT 规模架构(流式+KV Cache);Heap 新模式 — Kth Largest in Stream;Retry with Backoff;AI 新闻:GPT-5.6 + Fable 5 复出
周三 Jul 2 (Day 80)📚 复习日 — Trie + DFS、连接池与重试的正确分层、AI Chatbot 流式架构
周四 Jul 3 (Day 81)AI 聊天平台第三遍巩固;Last Stone Weight(Max-Heap 模拟);Circuit Breaker 保护下游;AI 监管新动态
周六 Jul 6 (Day 82+83)🔬 Saturday Deep Dive:堆专题(K Closest + 数据流中位数);专家级综合:多系统共存架构 + 技术债三方博弈 + Rate Limiting 实现

🧠 系统设计:核心要点 / System Design: Key Takeaways

本周系统设计以 AI 推理系统为主线,并以一节专家级综合收尾。

1. 流式输出是 LLM 系统的第一原则 / Streaming is non-negotiable for LLM systems

  • SSE(Server-Sent Events)单向流,CDN 友好,自带断线重连 — 比 WebSocket 更适合 LLM 推理的单向输出场景
  • Continuous Batching(vLLM):GPU 利用率从 30% → 80%+,这是 AI 系统降本的核心杠杆
  • Prefix KV Cache:对系统 prompt 的 attention key/value 预计算缓存,节省 ~20% 推理计算量

2. Session Affinity + 分层存储 / Session Routing + Tiered Storage

  • 用 conversation_id 做 Consistent Hashing,而不是 round-robin — 保证同一对话命中同一 GPU 上的 KV Cache
  • 分层存储策略:Redis(24h 热数据)→ PostgreSQL(30 天温数据)→ S3(历史冷数据)

3. 多系统共存的架构原则(Expert Synthesis)/ Multi-System Architecture Principles

三条跨系统设计的铁律:

  • 按一致性需求划分数据边界 — 支付系统(强一致性 ACID)绝不能与聊天/视频(最终一致性 Kafka)共用数据库
  • 共享无状态层,隔离有状态层 — API Gateway / Auth / CDN 可以共享;数据库和 Redis 必须隔离 key namespace
  • 故障域隔离(Bulkhead) — 每个系统独立连接池,Chat 流量洪峰不影响 Payment 可用性

三者的联系:LLM 推理系统本身就是一个极致的"读路径优化"案例 —— KV Cache、模型路由、SSE 流式,都是在已知 GPU 是唯一瓶颈的前提下,把整条链路的效率压榨到极限。这和 Autocomplete 系统的设计哲学(读多写少、内存优先)是同一类思维。


💻 算法:模式总结 / Algorithms: Patterns Mastered

本周完整开启了 Heap / Priority Queue 模式块(7 题),并用 Saturday Deep Dive 做了系统深挖。

堆模式核心模版 / Core Template

import heapq

heap = []
for item in stream:
    heapq.heappush(heap, item)
    if len(heap) > k:
        heapq.heappop(heap)
# heap[0] is the kth largest
题目变化点关键洞察
#703 Kth Largest in Stream (Day 79)基础模版,min-heap 大小为 k堆顶 = 第 k 大的门槛
#1046 Last Stone Weight (Day 81)Max-heap(取反),每次 pop 2 个Python 无 max-heap,全部取反模拟
#973 K Closest Points (Day 83)Key 是派生指标(距离),非原始值skip sqrt — 距离单调,不影响排序
#212 Word Search II (Day 78)Trie + DFS 终章找到单词后删除 Trie 叶节点是关键剪枝优化

Week 28 最重要的算法洞察:

Word Search II 的核心优化 —— 在 DFS 找到单词后,从 Trie 删除该叶节点(del node.children[ch])。这让 Trie 随着搜索过程越来越小,后续剪枝越来越激进。这是一个"搜索过程中动态优化搜索结构"的经典技巧。


🗣️ 软技能:重点练习 / Soft Skills: What to Practice

本周软技能聚焦在同一个高频题上:招聘与团队建设(Hiring & Growing a Team),并以周末的复合压力综合场景收尾。

核心分级框架 / Leveling Framework:

Junior: "我做了 X 次面试"
Senior: "我设计了面试流程,效率提升了 Y%"
Staff:  "我建立了跨团队可复用的招聘框架,并 mentor 了 3 个面试官"

本周提炼的三个关键信号(面试中要体现):

  1. 系统化,不只是个案 — 说"我建立了结构化 rubric 和 onboarding checklist",而不是"我帮了某个新人"
  2. 量化结果 — Offer 接受率从 40% → 68%、新人 2.5 个月独立上手(vs 行业平均 4-5 个月)
  3. 乘数效应思维 — 你的一次投入,让整个团队未来几年都更强

复合压力场景(Expert Synthesis)的核心框架:

技术债 + 团队士气 + 业务压力同时来袭时,最强的 Staff 级回答不是"我解决了一个难题",而是"我改变了问题被处理的方式":

  • 量化损失("上季度浪费了 9 工程师周")→ 说服 PM 和 CTO
  • 建立可持续机制(每个 Sprint 20% 时间还技术债)
  • 让进度可见(技术健康仪表盘:部署时间、P99、覆盖率)

需要继续练习的场景: 招聘故事需要更具体的数字和结构。建议准备一个完整的 STAR 稿,包含明确的 Situation 规模、Action 的三个具体步骤、以及两个量化 Result。


🎨 前端(Python Craft):知识巩固 / Python Craft: Concepts to Lock In

本周 Python Craft 完成了"弹性模式"三件套,是后端工程实战中最高频使用的组合:

三件套总览 / The Resilience Trio:

模式解决的问题关键参数
Connection Pooling (Day 78)TCP 握手重复开销(50-200ms × QPS)pool_size, max_overflow, pool_recycle + pool_pre_ping
Retry with Backoff (Day 79)瞬态失败(网络抖动、429、5xx)tenacity:wait_exponential + retry_if_exception_type
Circuit Breaker (Day 81)下游雪崩(超时堆积 → 级联崩溃)三状态:CLOSED → OPEN → HALF-OPEN

正确的分层顺序 (Day 80 复习题的核心洞察):

Retry (外层) └─ Connection Pool (内层) └─ 实际请求

Retry 包裹 Pool —— 失败时重新从池里借连接,而不是重试拿连接的动作。

Rate Limiting 实现(Day 83)— 快速自检:

  • Token Bucket:允许突发(burst),O(1) 内存,适合有合法流量峰值的 API
  • Sliding Window Log:精确,O(requests) 内存,适合严格 per-user 限制
  • 分布式版本:Redis sorted set + pipeline(原子性 + 低延迟)
  • 记住:只重试 5xx + 429,绝不重试 4xx

🤖 AI:本周知识点 / AI: What Stuck

本周 AI 内容跨越了两个维度:架构前沿(Day 78)和行业监管新动态(Day 79, 81, 83)。

架构前沿 / Architecture Frontier (Day 78 — CONCEPT)

三个正在重塑 AI 推理的新兴方向:

  • State Space Models (Mamba):O(N²) attention → O(N) linear recurrence,长序列场景的计算效率革命
  • Mixture of Experts (MoE):GPT-4 据报道是 MoE 架构(8 专家,每次激活 2 个)—— 参数量大、推理计算小
  • Test-Time Compute Scaling:o1/o3/DeepSeek-R1 的核心思路 —— 难题多给思考时间,而不是只追求更大的模型

注意:GPT-4 MoE 架构细节系据报道(据报道),非 OpenAI 官方确认。

AI 监管加速 / Regulatory Acceleration (Day 79, 81, 83 — NEWS)

本周最值得工程师关注的趋势 — 据报道:

  • GPT-5.6 Sol/Terra/Luna 以"政府审查先行"模式发布,~20 家机构优先访问,标志着前沿模型发布节奏正在制度化(据报道来自 The Next Web 等来源)
  • Anthropic Claude Fable 5 因出口管制审查暂停 19 天后复出,新增安全分类器(据报道)
  • Illinois 签署 AI 安全措施法;加州、纽约已有类似立法 —— 美国 AI 合规正从联邦缺位走向州级竞赛
  • 工程师的实际含义:前沿 API 访问不再稳定,需要 fallback 模型策略;"release day" ≠ "available day"

⚠️ 需要复习的内容 / What to Review

最弱的区域 / Weakest Areas:

  1. Heap 变体的细节区分 — 三道题看起来相似,但 Key 的含义完全不同:原始值 vs 派生距离 vs 需要返回原始点。建议对比 #703 / #1046 / #973 的代码结构,手写一遍模版变体。
  1. 软技能:招聘故事结构化 — 连续三天(Day 78/79/81)都是同一个问题。趁热打铁,现在花 30 分钟准备一个完整 STAR 稿。特别注意 Action 要分 3 个具体步骤,Result 要有 2+ 个量化指标。
  1. Rate Limiting 算法的使用场景 — Token Bucket vs Sliding Window Log vs Sliding Window Counter 的选择逻辑,尤其是"什么时候需要允许 burst"这个判断点,值得再过一遍。
  1. 系统设计:一致性边界 — Expert Synthesis 中"支付系统必须隔离"的直觉很重要。下次碰到 multi-system 设计题,第一个问题要是"哪个子系统对一致性要求最强?"

具体建议 / Specific Next Steps:

  • 在 30 分钟内手写 #973 K Closest Points 的完整解法(含 trace)
  • 准备招聘 STAR 故事稿(目标:2 分钟讲完,含 3 个 Action 和 2 个数字 Result)

🏆 本周亮点 / Win of the Week

Week 28 的最大亮点:AI 专题知识体系完整收官。

AI 模块(aiTopicIndex: 30/30)在本周正式画上句号,从最早的 Transformer 工作原理、Tokenization、Embeddings,到 RAG、Vector Databases、MoE,再到 AI Agents、Test-Time Compute、以及本周的 AGI 争论与监管新格局 —— 30 个 AI 话题形成了一条清晰的技术认知链。

加上 Saturday Deep Dive 完整攻克堆专题(K Closest + 数据流中位数双堆),以及 Expert Synthesis 将系统设计、软技能、Python Craft 三条线整合成一个完整的 Staff Engineer 思维框架 ——

这是整个 byte-by-byte 旅程中内容密度最高的一周。 🎯


🎯 下周预告 / Next Week Preview

根据当前进度(Day 83,Expert 阶段),下周将继续 Heap 模式块剩余题目,并开始 Greedy 或 Dynamic Programming 等新算法模式:

  • 算法 — Heap 剩余 4 题:#215 Kth Largest in Array、#621 Task Scheduler、#355 Design Twitter、#295 Find Median from Data Stream(双堆经典 Boss 战)
  • 系统设计 — Expert 阶段后续深化,可能包括更多分布式系统专题
  • Python Craft — 新的工程模式块开启(Week 8 预计进入 Testing 或 Observability 主题)
  • 软技能 — 新一轮行为问题(可能涵盖"技术决策复盘"或"跨团队对齐"场景)

特别关注:#295 Find Median from Data Stream 是 Heap 模式块的终极 Boss,双堆设计是面试高频考点,值得提前预习 NeetCode 视频。


Byte by byte, week by week. 🚀