package trie
import (
"fmt"
"math/rand"
"testing"
"time"
)
/*
前缀树
1.单个字符中,字符从前到后的加到一棵多叉数上
2.字符放在路上,节点是国内有专属的数据项(常见的是pass和end值[通过值和收尾值]) pass 当前节点被通过了几次, end 成为了多少个字符串的结尾
3.所有样本都这样添加,如果没有路就新建,如果有路就复用
4.沿途节点的pass值增加1,每个字符串结束时来到的节点end值加1
可以完成前缀相关的查询
["abc"], ["abd"], ["kst"]
每新加一个字符串都是 从头节点触发的
加一个节点[“ab"] ["a"]
[p:1,e:0] p: 5 e:1
a/ \k
[p:1, e:0] p:2 p:3 [p:1,e:0]
b/ \s
[p:1,e:0] p:2 p:3 e:1 [p:1,e:0]
c/ \d \t
[p:1,e:1] [p:1,e:1] [p:1,e:1]
如果所有字符串字符数量是N 建树的代价是 O(N),每处理一个字符代价是O(1)
"gab" 加入直接新建 不用考虑复用(压缩)
查询abd 字符串插入了几次
有没有走向 a 的路
有没有走向 b 的路
有没有走向 d 的路 查看e值
查询"ks" 有k 没有s
加入的所有字符串 有多少字符串以a 做前缀 p值就是 加入多少次,因为以我为路径加入的
字典树可以用于 不文明发言检测
*/
type Node1 struct {
pass int
end int
nexts []*Node1
}
func NewNode1() *Node1 {
return &Node1{
pass: 0,
end: 0,
// 0 --> 'a' 1 --> 'b' nexts[i] == nil i方向上的路不存在
// nexts[i] != nil i方向上的路存在
nexts: make([]*Node1,26), //如果加入的字符都是 a——z 小写的形式
}
}
type Trie1 struct {
root *Node1
}
func NewTrie1() *Trie1 {
return &Trie1{
root: NewNode1(),
}
}
func (t *Trie1)insert(word string) {
if word == "" {
return
}
node := t.root
node.pass++ // 有字符的话,必经过头结点
path := 0
for k := 0; k < len(word); k++ {
path = int(word[k] - 'a')
if node.nexts[path] == nil {
node.nexts[path] = NewNode1()
}
node = node.nexts[path]
node.pass++
}
node.end++
}
func (t *Trie1)delete(word string) {
if t.search(word) == 0 { // 查一下是否被加入,不删除一个未加入的字符串
return
}
node := t.root
node.pass--
path := 0
for k := 0; k < len(word); k++ {
path = int(word[k] - 'a')
if node.nexts[path].pass - 1 == 0 {
node.nexts[path] = nil
return
}
node.nexts[path].pass--
node = node.nexts[path]
}
node.end--
}
func (t *Trie1)search(word string) int { // word 被加入几次
if word == "" {
return 0
}
node, path := t.root, 0
for k := 0; k < len(word); k++ {
path = int(word[k] - 'a')
if node.nexts[path] == nil {
return 0
}
node = node.nexts[path]
}
return node.end
}
func (t *Trie1)prefixNumber(pre string) int { // pre 所有加入的字符串中,有几个是以pre这个字符串作为前缀
if pre == "" {
return 0
}
node, path := t.root, 0
for k := 0; k < len(pre); k++ {
path = int(pre[k] - 'a')
if node.nexts[path] == nil {
return 0
}
node = node.nexts[path]
}
return node.pass
}
func TestTrieInsert(t *testing.T) {
trie := NewTrie1()
trie.insert("helloworld")
fmt.Println(*trie.root.nexts['h'- 'a'].nexts['e'-'a'].nexts['l'-'a'])
}
func TestTrieSearch(t *testing.T) {
trie := NewTrie1()
trie.insert("helloworld")
trie.insert("helloworld")
fmt.Println(trie.search("helloworld"))
}
func TestTriePrefixNumber(t *testing.T) {
trie := NewTrie1()
trie.insert("helloworld")
trie.insert("helloworld")
trie.insert("hellow")
fmt.Println(trie.prefixNumber("hello"))
}
func TestTrieDelete(t *testing.T) {
trie := NewTrie1()
trie.insert("helloworld")
trie.insert("helloworld")
fmt.Println(trie.prefixNumber("hello"))
trie.delete("helloworld")
fmt.Println(trie.prefixNumber("hello"))
trie.delete("helloworld")
fmt.Println(trie.prefixNumber("hello"))
trie.delete("helloworld")
fmt.Println(trie.prefixNumber("hello"))
}
//-----------------------------------------------
type Node2 struct {
pass int
end int
nexts map[int]*Node2
}
func NewNode2() *Node2 {
return &Node2{
pass: 0,
end: 0,
nexts: map[int]*Node2{},
}
}
type Trie2 struct {
root *Node2
}
func NewTrie2() *Trie2 {
return &Trie2{
root: NewNode2(),
}
}
func (t *Trie2)insert(word string) {
if word == "" {
return
}
node := t.root
node.pass++
path := 0
for k := range word { //range 好处,可以不拆分汉字
path = int(word[k])
if node.nexts[path] == nil {
node.nexts[path] = NewNode2()
}
node = node.nexts[path]
node.pass++
}
node.end++
}
func (t *Trie2)delete(word string) {
if t.search(word) == 0 { // 查一下是否被加入,不删除一个未加入的字符串
return
}
node := t.root
node.pass--
path := 0
for k := range word {
path = int(word[k])
if node.nexts[path].pass - 1 == 0 {
node.nexts[path] = nil
return
}
node.nexts[path].pass--
node = node.nexts[path]
}
node.end--
}
func (t *Trie2)search(word string) int { // word 被加入几次
if word == "" {
return 0
}
node, path := t.root, 0
for k := range word {
path = int(word[k])
if node.nexts[path] == nil {
return 0
}
node = node.nexts[path]
}
return node.end
}
func (t *Trie2)prefixNumber(pre string) int { // pre 所有加入的字符串中,有几个是以pre这个字符串作为前缀
if pre == "" {
return 0
}
node, path := t.root, 0
for k := range pre {
path = int(pre[k])
if node.nexts[path] == nil {
return 0
}
node = node.nexts[path]
}
return node.pass
}
func TestTrieInsert2(t *testing.T) {
trie := NewTrie2()
trie.insert("helloworld")
fmt.Println(*trie.root.nexts['h'].nexts['e'].nexts['l'])
}
func TestTrieSearch2(t *testing.T) {
trie := NewTrie2()
trie.insert("helloworld")
trie.insert("helloworld")
fmt.Println(trie.search("helloworld"))
}
func TestTriePrefixNumber2(t *testing.T) {
trie := NewTrie2()
trie.insert("helloworld")
trie.insert("helloworld")
trie.insert("hellow")
fmt.Println(trie.prefixNumber("hello"))
}
func TestTrieDelete2(t *testing.T) {
trie := NewTrie2()
trie.insert("helloworld")
trie.insert("helloworld")
fmt.Println(trie.prefixNumber("hello"))
trie.delete("helloworld")
fmt.Println(trie.prefixNumber("hello"))
trie.delete("helloworld")
fmt.Println(trie.prefixNumber("hello"))
trie.delete("helloworld")
fmt.Println(trie.prefixNumber("hello"))
}
func TestTrie(t *testing.T) {
dic := []string{
"卧了个*",
"傻*",
"出门没吃药",
"违规词",
"尼玛,没吃药吗",
"长眼睛了吗",
}
trie := NewTrie2()
for k := range dic{
trie.insert(dic[k])
}
userSay := "尼玛"
if trie.prefixNumber(userSay) > 0 {
//违规词汇
}
fmt.Println(trie.search("卧了个槽"))
}
func Test(t *testing.T) {
compare()
}
func getRandomString(l int) string {
rand.Seed(time.Now().UnixNano())
length := rand.Int() % l
ans := ""
for i := 0; i < length; i++ {
str := string(rune('a' + (rand.Int() % 26)))
ans += str
}
return ans
}
func compare() {
t1, t2 := NewTrie1(), NewTrie2()
for i := 1; i < 100000;i++ {
randStr := getRandomString(100)
t1.insert(randStr)
t2.insert(randStr)
key := getRandomString(10)
if t1.search(key) != t2.search(key) {
fmt.Println(randStr,t1.search(key),t2.search(key))
panic("err")
}
if t1.prefixNumber(key) != t2.prefixNumber(key) {
fmt.Println(randStr,t1.prefixNumber(key),t2.prefixNumber(key))
panic("err")
}
del := getRandomString(10)
t1.delete(del)
t2.delete(del)
}
fmt.Println((*t1.root).nexts)
fmt.Println(t2.root.nexts)
}
trie——字典树
于 2021-10-24 19:16:36 首次发布