前言
我们每天在用搜索引擎搜索信息,相信大家肯定有注意过这样一个细节: 当输入某个字符的时候,搜索引框底下会出现多个推荐词,如下,输入关键字后底下会出现挺多以以输入内容为前缀的推荐搜索文本。它是如何实现的呢?
前缀树
原理介绍参考:轻松搞懂Trie树
Trie树是一种搜索树,也称字典树或单词查找树。此外也称前缀树,因为某节点的后代存在共同的前缀。它的key都为字符串,能做到高效查询和插入。时间复杂度为O(k),k为字符串长度。缺点是如果大量字符串没有共同前缀时很耗内存。它的核心思想就是减少没必要的字符比较,使查询高效率。即用空间换时间,再利用共同前缀来提高查询效率。
- 根节点不包含字符,其他节点每个节点只包含一个字符。
- 从根节点到某一节点经过路径的字符连起来即为该节点对应的字符串。
- 每个节点的所有子节点字符都不相同。
简单实现
库文件: t9.go
package t9
import "fmt"
type t9tree struct {
children []*t9tree
char byte
desc string
}
func NewT9() *t9tree {
return &t9tree{}
}
func (t *t9tree) Insert(start *t9tree, word []byte, desc string) *t9tree {
if len(word) == 0 {
return nil
}
if start == nil {
start = t
}
for _, node := range start.children {
if node.char == word[0] {
return t.Insert(node, word[1:], desc)
}
}
node := &t9tree{
char: word[0],
children: nil,
desc: "",
}
start.children = append(start.children, node)
if len(word) == 1 {
node.desc = desc
return node
}
return t.Insert(node, word[1:], desc)
}
func (t *t9tree) Walk() {
var _walk func(*t9tree)
var word []byte
if t.children == nil {
return
}
_walk = func(t *t9tree) {
if t == nil {
return
}
for _, node := range t.children {
word = append(word, node.char)
if len(node.children) == 0 {
// fmt.Println(string(node.char))
line := make([]byte, 0)
line = append(line, word...)
for len(line) < 25 {
line = append(line, ' ')
}
line = append(line, []byte("--- ")...)
line = append(line, []byte(node.desc)...)
fmt.Println(string(line))
}
_walk(node)
}
if (len(word)) > 0 {
word = word[:len(word)-1]
}
}
_walk(t)
}
主文件:demo.go
package main
import (
"demo/t9"
"fmt"
)
func main() {
t := t9.NewT9()
t.Insert(nil, []byte("dictionary"), "字典")
t.Insert(nil, []byte("translation"), "翻译")
t.Insert(nil, []byte("input"), "输入")
t.Insert(nil, []byte("output"), "输出")
t.Insert(nil, []byte("cpu"), "中央处理器")
t.Walk()
}
运行日志: