《STL源码分析》中关于红黑树(RB-tree)的描述引发的思考:

  1. 如何解决一个问题 一般而言,问题域会分解为下面几个方面: a. 期望结果是什么?可以是数据结果或行为结果;包含正常反馈与异常反馈。 b. 哪些基础信息提供?包含静态不变信息、动态信息输入、限定条件、从基础信息到期望结果的流程。 c. 如何规划业务数据?如何在业务数据上执行业务逻辑? a,b方面为业务先决条件,属于面向问题(面向客户)的调研、分析与设计。c方面为业务是否可实现、实现代价、性能评估,属于面向实现(面向开发)的分析与设计。c方面剥离大部分业务概念,可提取出一个或者多个核心抽象模型,模型与业务流程通过组合构成c方面的主干。

  2. 核心抽象模型 核心抽象模型此处指的是剥离绝大部分业务细节,提取的最贴近计算机的、贴近数学的数据与算法,可以分为以下几块: a. 原始数据 b. 数据使用限定 c. 结果数据 d. 基于原始数据的数据结构,亦或数据组织形式 e. 基于数据结构的运行规则(包含变换逻辑、限定规则等) f. 基于数据结构与运行规则的算法逻辑 a、b、c项倾向于数据的抽象;d、e、f项即使在a、b、c项完全相同时,考虑到CPU、IO、内存、耗时等性能指标,也可能不同。不妨把d、e、f取一个好听的名字:设计模式

(由于鄙人不善于记忆,往往以推导或故事的方式,所以文中存在一些假设性的推理,助记忆。)

tree(树)

树是一种常见的结构,往往通过递归的方式定义,非空树由一个根节点以及0个或n个子树构成,有明确的父子兄弟关系。无论是计算机语言,还是分析问题的思路(自顶而下分析法),甚至是日常所见,都充斥了树的思想,如下图所示: 树举例 往往越简单的东西越具有普遍适用性,但其解决问题的能力也相对较弱。对于树,若添加少量约束,可以覆盖其部分频繁使用的场景,并拥有不错的效率,那也是值得研究的。对于树的约束,大可以分为横向约束、纵向约束、数据约束,前两种约束了树的的形状,后一种约束了数据的排布。

binary tree(二叉树)

二叉树是对于树的横向约束。 对于树,一个节点下可以挂载0->n个子树,那么0->n选取哪个值作为边界最为恰当?

  • 0,限制子树小于或等于0无意义,只能构建仅有根节点的树;限制大于0无意义,叶子节点的子树必然为0,非叶子节点的子树必然不为0。维度:点
  • 1,限制子树小于或等于1无价值,构建出来的是个列表,使用列表算法更优;限制大于1无意义,同0场景。维度:1维
  • 2->n,限制子树小于或等于m,同父节点下,兄弟节点的关系最多有$\frac{m(m-1)}{2}$种情况,即m=2,关系数1;m=3,关系数3;m=4,关系数6…,关系数越多,算法所需要处理的场景越复杂;限制大于m,借鉴子树小于等于的场景,关系数至少为3,复杂度高(即使存在这样的场景,树的算法一般也能满足,树不能满足的场景下,必有额外的业务条件,需要定制算法,通用性不强。当然不排除找到合适的规则和算法后,这具有更高的效率。)。维度:m维

所以综合而言,定义一种树,叫二叉树,约束子树数量不超过2,在树横向约束的基础上进行特化(窃取了模板的概念),拥有适宜的灵活性、场景适应性以及较小的复杂度。(进一步特化,约束子树数量等于2,那便是完全二叉树。再进一步约束每层节点数都达到最大值,那便是满二叉树。)

binary search tree(二叉查找树)

二叉查找树是对于二叉树的数据约束。 二叉树有一个重要的应用,快速查找。要实现查找能力,那么作为二叉树节点的对象之间必须是可比较的,即,有明确的大于、小于与等于关系。节点的数据对象在树上需要按照一定规律进行排布,基于数据对象排布规则的算法可以实现数据对象的快速查找。于是,聪明的人们定义了一套规则,并把基于此规则的二叉树叫二叉查找树。

二叉查找树节点放置规则:任何节点的键值一定大于其左子树中每个节点的键值,并小于其右子树中每个节点的键值。

如上定义的二叉查找树便有了以下特征:

  • 顺着根节点,一直往左走,直到无左路可走,可获得最小值。
  • 顺着根节点,一直往右走,直到无右路可走,可获得最大值。
  • 首先遍历左子树,然后访问根节点,最后遍历右子树(中序遍历),可以获取到一个升序队列。
  • 二叉查找树的查找时间复杂度介于$O(log^N)$与O(N)之间。

如下为二叉查找树的示例:

ADT