面试记录 2026-10-09
题目:什么是红黑树?
难度:low
标签:algorithm/datastructure, general/knowledge
评分:⭐⭐☆☆☆(2/5)
我的回答
红黑树是 Java 中相对来说比较熟悉的一个数据结构,它在 JDK1.8 中将这个结构引入到 HashMap 中。在 HashMap 的节点长度超过 8,并且单个节点长度超过 8,并且它的数组长度超过 64,还是 16、64 的时候,才会将当前节点的链表升级为红黑树。通过红黑树对链表的链表进行改造,将原有链表改造为红黑树的结构,实现快速的搜索。而红黑树本身是一种自平衡的二叉树,它可以比较更,对比于链表,它可以更快的进行搜索,找到目标的值。红黑树本身是通过红节点与黑节点构成,所有叶子节点都是黑节点,整个树高不超过,平均树高是相等的。并且红黑树是自平衡的,搜索二叉树。
点评
优点:
- 抓住了和 HashMap 的关联,能说出转树条件(链长 8 + 桶数 64)
- 提到了"自平衡"和"叶子节点是黑色"这两个关键性质
- 能对比链表说明搜索优势
不足:
- 红黑树 5 条性质只说出 2 条:漏了①根节点必为黑;②红节点子节点必为黑(不能有连续红);③任一节点到叶子的所有路径黑节点数相同(不是"平均"高度相等,是黑高相等)
- 概念错位:把"平均树高等"表述错,实际是"最长路径不超过最短路径 2 倍",即高度受黑高约束 O(log n)
- 术语模糊:"搜索二叉树"讲不清楚——红黑树是自平衡二叉搜索树,本质仍是 BST + 平衡约束
- 未量化:链表查找 O(n)、红黑树 O(log n) 这种复杂度对比缺失
- 未说应用场景:TreeMap/TreeSet、Linux 内存管理、C++ std::map
补充:
- 5 条性质(口诀"根黑叶黑红隔、黑高相等"):①节点是红或黑;②根节点是黑色;③所有叶子(NIL)是黑色;④红节点的两个子节点都是黑色;⑤任一节点到其所有叶子路径上的黑节点数相同
- 为什么是红黑树而不是 AVL:AVL 严格平衡,增删时旋转更多;红黑树允许一定不平衡(最多 2:1),插入删除至多 2~3 次旋转,写多场景更优
- 典型应用:
TreeMap/TreeSet、ConcurrentHashMap JDK8、Linux CFS 调度器、Redis 的 zskiplist 思路(跳表是另一选择)
完整讲解
第一部分:知识重点剖析
核心考点定位:验证是否理解红黑树作为一种「工程折中」的自平衡 BST——既要能说出 5 条性质,又要能解释为什么工程上偏爱它而非 AVL。
知识点拆解:
- 底层原理:红黑树是自平衡二叉搜索树,通过「颜色约束」代替 AVL 的「严格高度差 ≤ 1」约束,允许最长路径不超过最短路径的 2 倍,本质上是把平衡的严格度换成了实现复杂度和写性能的收益。
- 5 条性质:①节点是红或黑;②根是黑色;③所有 NIL 叶子是黑色;④红节点的子节点必须是黑色(红不邻红);⑤任一节点到其所有叶子路径上的黑节点数相同(黑高相等)。
- 复杂度:查找/插入/删除均 O(log n);由于黑高 h ≥ 树高/2,树高 ≤ 2·log₂(n+1),写操作至多 2~3 次旋转。
- 设计权衡:相比 AVL 严格平衡(最多 O(log log n) 次旋转但更慢),红黑树牺牲少量查询效率换取更快的写入;相比跳表,红黑树内存紧凑、遍历有序。
- 工程实践:
TreeMap/TreeSet 直接用红黑树;ConcurrentHashMap JDK8 桶内链表超过 8 且桶数超过 64 时转红黑树(避免扩容抖动);Linux CFS 调度器用红黑树管理任务;std::map 基于红黑树。
按「底层原理 → 设计权衡 → 工程实践」递进排列。
第二部分:考察方向与延伸递进
考察方向:
- 深度:是否真正理解 5 条性质及其约束关系(比如"红不邻红"如何保证最长路径 ≤ 2 倍最短路径)
- 广度:能否对比 AVL、B+树、跳表、哈希表在不同场景的选型
- 应用:能否说清楚 JDK8 HashMap 为什么选红黑树(不是跳表),以及并发环境下的处理
延伸递进面试题:
- 为什么 HashMap JDK8 选红黑树而不是跳表?
- 解答要点:红黑树节点内存紧凑(无跳层层层指针),空间 O(n) vs 跳表平均 O(n) 但常数更大;且 HashMap 节点本身是数组/链表结构,转树改造侵入性小;跳表更适合需要范围查询或并发友好(Redis zset)。
- 红黑树插入一个红色节点后如何修复?
- 解答要点:分三种情况——叔叔红则父叔改黑、爷爷改红向上递归;叔叔黑则判断是左左/左右/右左/右右,做旋转(LL/RR 单旋转,LR/RL 双旋转),最后可能一路冒到根,根改黑。
- HashMap 为什么链表长度超过 8 才转树、桶数超过 64 才转树?
- 解答要点:泊松分布下自然概率链长达到 8 已经极小(约 0.00000006),说明大概率是哈希冲突而非随机分布,此时转树更划算;桶数少于 64 时优先扩容解决冲突比转树更便宜(摊平冲突)。
- TreeMap 和 HashMap 在什么场景下该选哪个?
- 解答要点:需要有序遍历、范围查询、K 值最近查询、排序取最大值 → TreeMap;只做 O(1) 键值映射、无顺序需求 → HashMap。写多场景 HashMap 更快。
第三部分:面试者参考回答(300-600 字口语化)
面试官您好,红黑树是一种自平衡的二叉搜索树,本质上是在普通 BST 上加了 5 条颜色约束。这 5 条性质是:第一,节点非红即黑;第二,根节点一定是黑色;第三,所有 NIL 叶子节点是黑色;第四,红节点的两个子节点必须是黑色,也就是"红不邻红";第五,从任一节点到它所有叶子路径上的黑节点数量必须相同,也就是"黑高相等"。正是第 5 条性质,配合"红不邻红",保证了最长路径不会超过最短路径的 2 倍,从而整个树的查找、插入、删除复杂度都是 O(log n)。
它相比 AVL 树的优势在于不追求严格平衡,只约束红黑比例,所以插入删除时最多 2 到 3 次旋转就能修复,写操作比 AVL 更快。工程上我们更关心"读多还是写多":读多严格平衡用 AVL,写多折中用红黑树,Java 生态里几乎所有标准有序结构都选了红黑树——比如 TreeMap、TreeSet,Linux 的 CFS 调度器,还有 C++ 的 std::map。
回到我们熟悉的 JDK 8 HashMap:当一个桶里的链表长度超过 8、并且整个 HashMap 的桶数组长度超过 64 时,链表会转换成红黑树。这里有两个门槛,第一个 8 来自泊松分布,随机情况下链长达到 8 的概率极低,说明大概率是哈希冲突而非分布问题;第二个 64 是保证扩容比转树更划算。转成红黑树之后,查找从 O(n) 降到 O(log n),避免了极端哈希碰撞下链表退化带来的性能陷阱。