1 定义
Trie
树,即字典树,又称前缀树,是一种树形结构,是一种哈希树的变种,像字典一样的树。

Trie
的核心思想是空间换时间。利用字符串的公共前缀来降低查询时间的开销以达到提高效率的目的,使用Trie
相比哈希查询的好处是,路由算法中需要模糊匹配,哈希是做不到的,它只能精确查找。
2 实现
2.1 字段
1 2 3 4 5 6
| class Trie{ public: int tag; Trie *nxt[26]{}; Trie(): tag(0) {} };
|
首先定义核心字段,tag
用来表示当前节点是否是某个字符串的结尾,比如说如果在上面的树中,又来了一个go
,那在第一个o
中的tag
应该标识为1
2.2 插入
1 2 3 4 5 6 7 8 9 10
| void insert(const string &s) { Trie *p = this; for (char i : s) { if (p->nxt[i - 'a'] == nullptr) { p->nxt[i - 'a'] = new Trie(); } p = p->nxt[i - 'a']; } p->tag = 1; }
|
如果子节点不存在,则新建一个子节点
移动p指针
- 将最后一个字符的
tag
置为1,标识一个字符串的末尾
2.3 查询
1 2 3 4 5 6 7 8 9 10
| bool search(const string &s) { Trie *p = this; for (char i : s) { if (p->nxt[i - 'a'] == nullptr) { return false; } p = p->nxt[i - 'a']; } return p->tag; }
|
- 如果遍历到子节点为
nullptr
,则直接返回false
- 移动
p
指针
- 判断最后一个
p->tag
是否为1
2.4 前缀匹配
1 2 3 4 5 6 7 8 9
| bool startsWith(const string &prefix) { Trie *p = this; for(char i : prefix) { if(p->nxt[i-'a'] == nullptr) return false; p = p->nxt[i-'a']; } return true; }
|
整个类的完整实现如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| class Trie{ public: int tag; Trie *nxt[26]{}; Trie(): tag(0) {} void insert(const string &s) { Trie *p = this; for (char i : s) { if (p->nxt[i - 'a'] == nullptr) { p->nxt[i - 'a'] = new Trie(); } p = p->nxt[i - 'a']; } p->tag = 1; } bool search(const string &s) { Trie *p = this; for (char i : s) { if (p->nxt[i - 'a'] == nullptr) { return false; } p = p->nxt[i - 'a']; } return p->tag; }
bool startsWith(const string &prefix) { Trie *p = this; for(char i : prefix) { if(p->nxt[i-'a'] == nullptr) return false; p = p->nxt[i-'a']; } return true; } };
|
下面提供一个go
实现的版本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| type Trie struct { nxt [26]*Trie tag int }
func (this *Trie) Insert(word string) { root := this for _, c := range word { if root.nxt[c-'a'] == nil { root.nxt[c-'a'] = &Trie{} } root = root.nxt[c-'a'] } root.tag = 1 }
func (this *Trie) Search(word string) bool { root := this for _, c := range word { if root.nxt[c-'a'] == nil { return false } root = root.nxt[c-'a'] } return root.tag == 1 }
func (this *Trie) StartsWith(prefix string) bool { root := this for _, c := range prefix { if root.nxt[c-'a'] == nil { return false } root = root.nxt[c-'a'] } return true }
|
想要做题的话可以去试试这个 leetcode 208. 实现 Trie (前缀树)
3 radix tree
gin
框架并不是使用普通的trie
树,而是一种经过压缩的Radix Trie
又叫做基数树、基数特里树、压缩前缀树。
当某个父节点仅有一个子节点,并且不存在字符串以该父节点为结尾的时候,会将父节点和子节点进行合并,则最前面的trie
树就会变成下图:

Reference:
HTTP Router 算法演进