mirror of
https://github.com/chai2010/advanced-go-programming-book.git
synced 2025-05-23 20:02:22 +00:00
fix router section
This commit is contained in:
parent
2e22cc595b
commit
6f083a9fb9
@ -109,12 +109,16 @@ httprouter和众多衍生router使用的数据结构被称为压缩字典树(Rad
|
||||
|
||||

|
||||
|
||||
*图 6-1 字典树*
|
||||
|
||||
字典树常用来进行字符串检索,例如用给定的字符串序列建立字典树。对于目标字符串,只要从根节点开始深度优先搜索,即可判断出该字符串是否曾经出现过,时间复杂度为O(n),n可以认为是目标字符串的长度。为什么要这样做?字符串本身不像数值类型可以进行数值比较,两个字符串对比的时间复杂度取决于字符串长度。如果不用字典树来完成上述功能,要对历史字符串进行排序,再利用二分查找之类的算法去搜索,时间复杂度只高不低。可认为字典树是一种空间换时间的典型做法。
|
||||
|
||||
普通的字典树有一个比较明显的缺点,就是每个字母都需要建立一个孩子节点,这样会导致字典树的层数比较深,压缩字典树相对好地平衡了字典树的优点和缺点。下图是典型的压缩字典树结构:
|
||||
|
||||

|
||||
|
||||
*图 6-2 压缩字典树*
|
||||
|
||||
每个节点上不只存储一个字母了,这也是压缩字典树中“压缩”的主要含义。使用压缩字典树可以减少树的层数,同时因为每个节点上数据存储也比通常的字典树要多,所以程序的局部性较好(一个节点的path加载到 cache 即可进行多个字符的对比),从而对CPU缓存友好。
|
||||
|
||||
## 5.2.3 压缩字典树创建过程
|
||||
@ -171,8 +175,11 @@ r.PUT("/user/installations/:installation_id/repositories/:reposit", Hello)
|
||||
```
|
||||
|
||||
这样PUT对应的根节点就会被创建出来。把这棵PUT的树画出来:
|
||||
|
||||

|
||||
|
||||
*图 6-3 插入路由之后的压缩字典树*
|
||||
|
||||
radix的节点类型为`*httprouter.node`,为了说明方便,我们留下了目前关心的几个字段:
|
||||
|
||||
```
|
||||
@ -194,31 +201,40 @@ indices: 子节点索引,当子节点为非参数类型,即本节点的 wild
|
||||
|
||||
### 5.2.3.2 子节点插入
|
||||
|
||||
当插入`GET /marketplace_listing/plans`时,类似前面PUT的过程,GET树的结构如图所示:
|
||||
当插入`GET /marketplace_listing/plans`时,类似前面PUT的过程,GET树的结构如*图 6-4*:
|
||||
|
||||

|
||||
|
||||
*图 6-4 插入第一个节点的压缩字典树*
|
||||
|
||||
因为第一个路由没有参数,path都被存储到根节点上了。所以只有一个节点。
|
||||
|
||||
然后插入 `GET /marketplace_listing/plans/:id/accounts`,新的路径与之前的路径有共同的前缀,且可以直接在之前叶子节点后进行插入,那么结果也很简单,插入后树变成了这样:
|
||||
然后插入 `GET /marketplace_listing/plans/:id/accounts`,新的路径与之前的路径有共同的前缀,且可以直接在之前叶子节点后进行插入,那么结果也很简单,插入后的树结构见*图 6-5*:
|
||||
|
||||

|
||||
|
||||
*图 6-5 插入第二个节点的压缩字典树*
|
||||
|
||||
由于`:id`这个节点只有一个字符串的普通子节点,所以indices还依然不需要处理。
|
||||
|
||||
上面这种情况比较简单,新的路由可以直接作为原路由的子节点进行插入。实际情况不会这么美好。
|
||||
|
||||
### 5.2.3.3 边分裂
|
||||
|
||||
接下来我们插入`GET /search`,这时会导致树的边分裂。
|
||||
接下来我们插入`GET /search`,这时会导致树的边分裂,见*图 6-6*。
|
||||
|
||||

|
||||
|
||||
*图 6-6 插入第三个节点,导致边分裂*
|
||||
|
||||
原有路径和新的路径在初始的`/`位置发生分裂,这样需要把原有的root节点内容下移,再将新路由 `search`同样作为子节点挂在root节点之下。这时候因为子节点出现多个,root节点的indices提供子节点索引,这时候该字段就需要派上用场了。"ms" 代表子节点的首字母分别为m(marketplace)和s(search)。
|
||||
|
||||
我们一口作气,把`GET /status`和`GET /support`也插入到树中。这时候会导致在`search`节点上再次发生分裂,来看看最终的结果:
|
||||
我们一口作气,把`GET /status`和`GET /support`也插入到树中。这时候会导致在`search`节点上再次发生分裂,最终结果见*图 6-7*:
|
||||
|
||||

|
||||
|
||||
*图 6-7 插入所有路由后的压缩字典树*
|
||||
|
||||
### 5.2.3.4 子节点冲突处理
|
||||
|
||||
在路由本身只有字符串的情况下,不会发生任何冲突。只有当路由中含有wildcard(类似 :id)或者catchAll的情况下才可能冲突。这一点在前面已经提到了。
|
||||
|
Loading…
x
Reference in New Issue
Block a user