Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

删除节点与树的退化

复习

  • 二叉搜索树:左子树都小、右子树都大
  • 二叉树与递归遍历:中序遍历得到有序序列
  • 链表:可能退化成一条线

TL;DR

  • 删除 BST 节点分三种情况:叶子、只有一个子、有两个子
  • 有两个子节点时,通常用中序后继或前驱来替代
  • 如果插入顺序不好,BST 会退化成链表
  • 退化成链表后,查找就变成 O(n)

正文

  二叉搜索树的查找、插入都很顺,但删除要麻烦一些。麻烦在哪?在于删掉一个节点后,还得维持那条“左小右大”的规则

三种情况

  删除一个节点,按它的孩子情况分三种:

  • 叶子节点:直接删掉即可,不影响别人
  • 只有一个子节点:把这个子节点“提上来”,接替它的位置
  • 有两个子节点:最麻烦,不能直接删

两个子节点怎么办

  如果一个节点既有左孩子又有右孩子,直接删掉就会留下两个“孤儿”,接替位置的规则也说不清。

  常见的做法是:找一个合适的节点来替代它。通常选中序后继(右子树里最小的那个),或者中序前驱(左子树里最大的那个)。把这个替代节点的值搬到要删的位置,再把它自己按前两种情况删掉。

  为什么选中序后继?因为它比左子树都大、又比其他右子树都小,放到原位刚好满足规则。找到正确的“接班人”,是删除能保持有序的关键。

树会长歪

  不过,二叉搜索树有一个隐患:它的形状,完全取决于插入顺序

  如果数据恰好按从小到大(或从大到小)的顺序插入,每个新节点都会一路往同一个方向挂,树就长成了一条斜线——退化成了链表

  一旦退化成链表,高度变成 O(n),查找也就成了 O(n)。原本 O(log n) 的优势荡然无存。同样的数据,换个插入顺序,性能可能一个天上一个地下。

  这说明:二叉搜索树的效率,并不由数据本身保证,而“树够不够平衡”才是关键。怎样在插入删除的过程中,始终把树维持得矮而匀称?下一章来解决。

思考题 1

  删除一个有两个子节点的 BST 节点,为什么不能直接删掉?

思考题 2

  在什么情况下,二叉搜索树会退化成链表?

小结

知识点

  • BST 删除分叶子、单子、双子三种情况
  • 双子节点通常用中序后继或前驱替代
  • 插入顺序不当会导致树退化
  • 退化成链表后,查找变为 O(n)

参考资料

  1. Wikipedia(zh):二叉搜索树:包含删除节点的处理方式
  2. Wikipedia(zh):树的遍历:中序前驱与后继

思考题答案(仅供参考)

思考题 1

  因为它有两个子节点,直接删会留下两棵子树无人接替,而且需要维持“左小右大”的规则。正确做法是找一个合适的节点(如中序后继)来顶替其值,再把这个顶替节点本身删掉。

思考题 2

  当插入的数据基本有序时,每个新节点都会沿着同一个方向挂上去,树会变成一条斜线,也就是退化成链表。这时高度为 O(n),查找也随之变为 O(n)

协议

  本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

封面图

设计师 | 南国微雪