这篇文章主要讲解STL源码剖析 P.224 P.229 中的find()和insert_unique()函数 . 这两个函数写的很巧妙 , 理解有点绕 . 网上都找不到讲解 , 都TM一堆抄书的.
注意事项
所使用的代码非书上代码 , 使用gcc代码/usr/include/c++/7/bits/stl_tree.h来说明(基本原理没变)
说明时我会把变量的前两个下划线去掉 , 打起来太烦了
- 在比较结点大小时 , 会使用_M_key_compare函数 , 这是一个仿函数 , 这里做一个约定 , 当这个函数返回 1 时 , 定义为前者比后者”小” , 这个”小”是抽象的小 , 下文表示>或<时都表示抽象的大或小 , 具体实现取决于_Key_compare . 如果将 1 定义为大的话 , - - 和 ++ 会是反向的.
源代码
_M_get_insert_unique_pos()
1 | template<typename _Key, typename _Val, typename _KeyOfValue, |
find()
1 | template<typename _Key, typename _Val, typename _KeyOfValue, |
_M_lower_bound()
1 | //这个函数在find()的第8行调用 |
_M_insert_node
1 | //放这个代码是为了说明我们定义的"大"会在左边 |
find()函数
find()函数第8行直接调用_M_lower_bound()函数 , 即找出要插入的变量 k 的下界.
进入_M_lower_bound()函数 , x 是 root 结点(_M_begin()) , y 是 end(header) , 只要 x 不为 0 , 就将 x 与 k 进行比较 , 如果 k ≤ x , 将 x 保存到 y 当中 , 同时向左找(小的在左边 , 可以在_M_insert_node函数中找到) . 当 x == 0 时 , 这个时候 y 不是保存的 x 的父结点 , 而是比 k 大的最小值 . 返回那个结点的迭代器.
find()函数第9行 , 第一个判断 j == end() , 我们可以知道只有当不存在 x 使 k ≤ x 时 , j 才为 end() , 即要寻找的值大于整个树的最大值 , 那当然不存在这样的值 , 返回 end().
第10和第11行第二个判断 , 此时 j 不为 end() . 下面就是精彩的地方了.
分为两种情况.
当 _M_key_compare(__k,_S_key(__j._M_node)) 返回 1 时 , 可知 j > k , 从_M_lower_bound()函数中 k ≤ j , 那么一定不存在 k (有些人可能考虑 j 的父结点是否可能等于 k , 见特殊说明1).
当 _M_key_compare(__k,_S_key(__j._M_node)) 返回 0 时 , 可知 j ≤ k , 从_M_lower_bound()函数中 k ≤ j , k 既大于等于 j , 又小于等于 j , 那么唯一的可能就是 k == j . 返回 j 所在的结点的迭代器.
insert_unique()
这个函数在GCC版本里改名为_M_get_insert_unique_pos() , 不过逻辑没改 , 返回值有所变动.
这个函数逻辑上和find()很相似 , 上面懂了的话这个应该也能看懂.
在第14到19行中一直在比较大小 , 我们可以看到 y 是插入点的父结点 , comp 存储了 k 与 x 的大小关系.
这里同样也要分为两种情况.
当 comp 为 1 时 , 如果 j == begin() , 即 k 比整个树最小值还小 , 那么可以插入 . 如果不等于begin() , 将 j 变为 j- - , 即比 j 小的那个数(j- - 与 j 整体大小上相邻) . 此时再比较 j- - 与 k 的大小 , 如果 k > j- - , 又根据 comp 为 1 可以看出 j > k , 也就是说 k 位于 j- - 与 j 之间 , 那么必然是唯一的 . 如果 j- - ≥ k , 见特殊说明3.
当 comp 为 0 时 . 即 k ≥ j , 此时再比较 j 与 k 大小 , 如果 k > j , 那么唯一(有些人可能会考虑 j 的父结点等于 k , 见特殊说明2). 否则的话即 j ≥ k , 此时 j == k , 不唯一.
特殊说明
如果 j 的某个父结点等于 k , 那么那个时候会向左 , 而左边的所有结点都比 k 小 , 最后比较时不可能出现 j > k 的情况 . 即一路上不重复且正确方向到底.
如果 j 的某个父结点等于 k , 那么那个时候会向右 , 而右边的所有结点都比 k 大 , 最后比较时不可能出现 k > j 的情况 . 即一路上不重复且正确方向到底.
j ≥ k and j- - ≥ k , 比 j 小的 j- - 只能出现在 j 的左子树和不为左结点的父结点的父结点(设为 M ) , 因为 x == 0 , 所以左子树为空 , 而 j 是在 M 的右结点的左结点下 , 即 k ≥ j- - , 此时可得 k 不唯一.