当前位置:   article > 正文

Trie 树——Golang实现_golang trie

golang trie

Trie 树,也叫“字典树”。顾名思义,它是一个树形结构。它是一种专门处理字符串匹配的数据结构,用来解决在一组字符串集合中快速查找某个字符串的问题。

现在,我们先来看下,Trie 树到底长什么样子。

我举个简单的例子来说明一下。我们有 6 个字符串,它们分别是:how,hi,her,hello,so,see。我们希望在里面多次查找某个字符串是否存在。如果每次查找,都是拿要查找的字符串跟这 6 个字符串依次进行字符串匹配,那效率就比较低,有没有更高效的方法呢?

这个时候,我们就可以先对这 6 个字符串做一下预处理,组织成 Trie 树的结构,之后每次查找,都是在 Trie 树中进行匹配查找。Trie 树的本质,就是利用字符串之间的公共前缀,将重复的前缀合并在一起。最后构造出来的就是下面这个图中的样子。

其中,根节点不包含任何信息。每个节点表示一个字符串中的字符,从根节点到红色节点的一条路径表示一个字符串(注意:红色节点并不都是叶子节点)。

 为了让你更容易理解 Trie 树是怎么构造出来的,我画了一个 Trie 树构造的分解过程。构造过程的每一步,都相当于往 Trie 树中插入一个字符串。当所有字符串都插入完成之后,Trie 树就构造好了。

 当我们在 Trie 树中查找一个字符串的时候,比如查找字符串“her”,那我们将要查找的字符串分割成单个的字符 h,e,r,然后从 Trie 树的根节点开始匹配。如图所示,绿色的路径就是在 Trie 树中匹配的路径。

 如果我们要查找的是字符串“he”呢?我们还用上面同样的方法,从根节点开始,沿着某条路径来匹配,如图所示,绿色的路径,是字符串“he”匹配的路径。但是,路径的最后一个节点“e”并不是红色的。也就是说,“he”是某个字符串的前缀子串,但并不能完全匹配任何字符串。

Golang实现

1、基于数组的代码实现

以下代码用于判断a-z小写的单词是否存在

  1. package main
  2. import "fmt"
  3. const Size = 26 //数组大小为26
  4. type TireNode struct {
  5. Data uint8
  6. IsWord bool
  7. Next []*TireNode
  8. }
  9. type TireHead struct {
  10. Len int
  11. Next []*TireNode
  12. }
  13. func NewTire() *TireHead {
  14. //next := make([]*TireNode, Size, Size)
  15. head := &TireHead{
  16. Len: 0,
  17. Next: nil,
  18. }
  19. return head
  20. }
  21. func (t *TireHead) Insert(str string) {
  22. //index := str[0] - 'a'
  23. if t.Next == nil {
  24. t.Next = make([]*TireNode, Size, Size)
  25. }
  26. cur := t.Next
  27. index := uint8(0)
  28. for i := 0; i < len(str); i++ {
  29. index = str[i] - 'a' //只能比较 a-z的Size个字符(26个)
  30. if cur[index] == nil { //TODO 注意:如果节点为空,需要创建
  31. newNode := &TireNode{
  32. Data: str[i],
  33. IsWord: false,
  34. Next: make([]*TireNode, Size, Size),
  35. }
  36. cur[index] = newNode
  37. } else {
  38. cur[index].Data = str[i] //todo 如果节点已经有了,就直接使用可以了
  39. }
  40. if i == len(str)-1 {
  41. cur[index].IsWord = true //todo 最后一个字符对应的 标志位需要设置为true,代表是一个完整的字符
  42. }
  43. cur = cur[index].Next
  44. }
  45. }
  46. func (t *TireHead) isExist(str string) bool {
  47. if t.Next == nil {
  48. return false
  49. }
  50. cur := t.Next
  51. index := uint8(0)
  52. for i := 0; i < len(str); i++ {
  53. index = str[i] - 'a'
  54. if cur[index] == nil {
  55. //todo 特别要注意条件的判断:如果找到对应的内容为nil了,就直接返回。
  56. // 不然从nil对应的变量中取数据,包报错
  57. return false
  58. }
  59. if cur[index].Data != str[i] {
  60. return false
  61. }
  62. if i == len(str)-1 && cur[index].IsWord == true {
  63. return true
  64. }
  65. cur = cur[index].Next
  66. if cur == nil {
  67. return false
  68. }
  69. }
  70. return false
  71. }
  72. func main() {
  73. tire := NewTire()
  74. //tire.Insert("ab")
  75. //tire.Insert("ok")
  76. tire.Insert("he")
  77. tire.Insert("ok")
  78. tire.Insert("hello")
  79. fmt.Println("he:",tire.isExist("a"))
  80. fmt.Println("ok:",tire.isExist("ok"))
  81. fmt.Println("hel:",tire.isExist("hel"))
  82. }

2、基于hashmap的代码实现

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. //数据节点;存储数据:数据值、是否结束标志位、下一个节点指针
  6. type TNode struct {
  7. Data byte
  8. IsWord bool
  9. Next map[byte]*TNode
  10. }
  11. //根节点,存储数据为:1、字符串个数;2、数据节点map序列
  12. type TNRoot struct {
  13. Len int
  14. Node map[byte]*TNode
  15. }
  16. func CreateTire() TNRoot {
  17. tn := make(map[byte]*TNode)
  18. //创建节点的时候,需要将node赋值
  19. tRoot := TNRoot{
  20. Len: 0,
  21. Node: tn, //todo 技巧:需要赋值,不然后续没有办法操作
  22. }
  23. return tRoot
  24. }
  25. func (tn *TNRoot) insert(str string) {
  26. cur := tn.Node
  27. for i := 0; i < len(str); i++ {
  28. if cur[str[i]] == nil {
  29. newNode := &TNode{
  30. Data: str[i],
  31. IsWord: false,
  32. Next: make(map[byte]*TNode), //TODO 技巧:下一个节点需要实例化,不然是空,后续无法操作。
  33. }
  34. cur[str[i]] = newNode
  35. }
  36. if i == len(str)-1 {
  37. cur[str[i]].IsWord = true
  38. }
  39. cur = cur[str[i]].Next
  40. }
  41. tn.Len++
  42. }
  43. func (tn *TNRoot) isExist(str string) bool {
  44. if tn.Node == nil {
  45. return false
  46. }
  47. cur := tn.Node
  48. for i := 0; i < len(str); i++ {
  49. if cur[str[i]] == nil { //如果比较的数据不存在,直接返回
  50. return false
  51. }
  52. if cur[str[i]].Data != str[i] {
  53. return false
  54. }
  55. if i == len(str)-1 { //判断最后一个比较的字符是否是结束标志位
  56. if cur[str[i]].IsWord == true {
  57. return true
  58. }
  59. }
  60. cur = cur[str[i]].Next
  61. }
  62. return false
  63. }
  64. func main() {
  65. tire := CreateTire()
  66. tire.insert("hello")
  67. tire.insert("ok")
  68. tire.insert("he")
  69. fmt.Println(tire.isExist("ok"))
  70. fmt.Println(tire.isExist("he"))
  71. fmt.Println(tire.isExist("hel"))
  72. fmt.Println("tire树的字符串数量:", tire.Len)
  73. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/代码探险家/article/detail/994690
推荐阅读
  

闽ICP备14008679号