从 Splay 乐队到伸展树:技术人的“无用”爱好如何留下长期回报

2026-08-20 46 预计阅读时间: 1 分钟
来源: oschina.net AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:8 分钟

一支由清华大学计算机系学生在 2013 年组建的校园摇滚乐队,因为名为 Splay,再次进入公众视野。这个名字来自数据结构中的伸展树(Splay Tree):它不严格维持全局平衡,却会把刚访问过的节点旋转到根部。音乐与算法看似毫不相干,但放在一起看,恰好说明了一件事:很多当下无法量化的投入,会在表达能力、协作方式和个人辨识度上留下长期回报。

“有用”不是兴趣的唯一衡量单位

从结果看,校园乐队可能只留下几首歌、一次比赛成绩和一些旧录音。若只用职业晋升、论文数量或商业收入衡量,它很容易被归入“没什么用”。

但乐队排练包含一组工程师熟悉的能力:在约束下创作、快速暴露错误、与他人同步节奏,以及把不完整的想法打磨成可以公开呈现的作品。代码评审、系统联调和现场演出并不等价,却共享一种工作方式:个人能力只有进入协作系统,才会形成完整输出。

这也解释了为什么技术人的非技术经历常常具有延迟价值。它未必直接生成一项职业技能,却可能改善一个人如何讲述复杂问题、如何接受反馈,以及如何在不确定性中持续完成作品。

Splay 这个名字为什么贴切

伸展树是一种二叉搜索树。每次查询、插入或删除后,它都会通过旋转把相关节点移动到根附近。它不保存红黑树那样明确的平衡标记,也不能保证单次操作一定是 O(log n);极端情况下,单次操作可能达到 O(n)。但在一系列操作上,它具有均摊 O(log n) 的性能。

更重要的是,它会偏爱近期访问过的数据。如果访问具有局部性,热点节点会逐渐靠近根部,后续查询因而更快。这种结构没有追求“每一刻都绝对整齐”,而是根据真实访问不断调整自己。

把乐队命名为 Splay,可以读出一种计算机系学生式的幽默。进一步说,它也提供了一个理解兴趣投入的隐喻:一次排练或一首歌未必立刻改变什么,但反复访问某种能力,会让它逐渐靠近个人能力结构的“根”。

动手观察伸展树如何记住热点

下面是一个可直接运行的最小 Python 示例。它实现插入、查询和伸展操作,并连续访问同一个键,观察热点节点如何移动到根部。将代码保存为 splay_demo.py,使用 Python 3 运行即可。

from dataclasses import dataclass
from typing import Optional


@dataclass
class Node:
    key: int
    left: Optional["Node"] = None
    right: Optional["Node"] = None


def rotate_right(root: Node) -> Node:
    new_root = root.left
    assert new_root is not None
    root.left = new_root.right
    new_root.right = root
    return new_root


def rotate_left(root: Node) -> Node:
    new_root = root.right
    assert new_root is not None
    root.right = new_root.left
    new_root.left = root
    return new_root


def splay(root: Optional[Node], key: int) -> Optional[Node]:
    if root is None or root.key == key:
        return root

    if key < root.key:
        if root.left is None:
            return root
        if key < root.left.key:
            root.left.left = splay(root.left.left, key)
            root = rotate_right(root)
        elif key > root.left.key:
            root.left.right = splay(root.left.right, key)
            if root.left.right is not None:
                root.left = rotate_left(root.left)
        return rotate_right(root) if root.left is not None else root

    if root.right is None:
        return root
    if key > root.right.key:
        root.right.right = splay(root.right.right, key)
        root = rotate_left(root)
    elif key < root.right.key:
        root.right.left = splay(root.right.left, key)
        if root.right.left is not None:
            root.right = rotate_right(root.right)
    return rotate_left(root) if root.right is not None else root


def insert(root: Optional[Node], key: int) -> Node:
    if root is None:
        return Node(key)

    root = splay(root, key)
    assert root is not None
    if root.key == key:
        return root

    node = Node(key)
    if key < root.key:
        node.left, node.right = root.left, root
        root.left = None
    else:
        node.left, node.right = root, root.right
        root.right = None
    return node


root = None
for value in [40, 20, 60, 10, 30, 50, 70]:
    root = insert(root, value)

for query in [20, 60, 20, 20]:
    root = splay(root, query)
    print(f"访问 {query:>2} 后,根节点是 {root.key}")

运行命令:

python3 splay_demo.py

每次成功查询后,被访问的键都会成为根节点。真实实现还需要补充删除、重复键策略、测试和性能基准;这里的代码只用于展示伸展过程。对于要求稳定尾延迟的服务,伸展树的单次最坏复杂度需要谨慎评估,不能仅凭均摊复杂度做选型。

把“无用”爱好经营成可复用资产

技术人不必强行把每个爱好包装成生产力工具。更实际的做法,是保留兴趣本身,同时让过程产生可复用的结果:完成一首歌,而不只是练习片段;组织一次演出,而不只是讨论创意;为作品留下版本、说明和可公开访问的归档。

判断一项长期投入是否值得,可以检查四件事:

  • 它是否促使你持续完成作品,而非只消费内容;
  • 它是否让你与不同能力的人协作;
  • 它是否提供真实、及时且无法回避的反馈;
  • 即使没有职业回报,你是否仍愿意承担时间成本。

摇滚未必需要证明自己“有用”。但当一段校园经历多年后仍能让人记住一个名字、一首作品和一个数据结构时,它已经产生了比短期指标更复杂的价值。像伸展树一样,人的能力也会被一次次访问重新排列;真正影响长期路径的,往往正是那些被持续调用的兴趣。


相关推荐