Go語言資料結構與演算法-Trie樹
阿新 • • 發佈:2022-02-19
Trie樹
概述
Trie樹,又叫字典樹、字首樹(Prefix Tree)、單詞查詢樹或鍵樹,是一種很常用的樹結構【多叉樹】。
它被廣泛用於各個方面,比如字串檢索、中文分詞、求字串最長公共字首和字典排序等等。
核心思想
空間換時間:資料結構本身比較消耗空間。所有子節點都有一個共同的字首,但它利用了字串的共同字首(Common Prefix)作為儲存依據,以此來節省儲存空間,並加速搜尋時間。
Trie 的字串搜尋時間複雜度為 O(m),m 為字串的長度,其查詢效能與集合中的字串的數量無關。其在搜尋字串時表現出的高效,使得特別適用於構建文字搜尋和詞頻統計等應用。
通過上圖展示得到Trie樹關鍵字集合{"分散", "分散精力", "分散投資", "分散式", "工程", "工程師"}
Trie樹的基本性質
- 根節點不包含字元,除根節點外的每一個子節點都包含一個字元。
- 從根節點到某一個節點,路徑上經過的字元連線起來,為該節點對應的字串。
- 每個節點的所有子節點包含的字元互不相同。
示例程式碼
package main import "fmt" // Node 子節點 type Node struct { World rune // 當前節點儲存的字元。byte只能表示英文字元,rune可以表示任意字元 Child map[rune]*Node // 子節點,用一個map儲存 Term string // 標記關鍵詞 } // TrieTree Trie樹根節點 type TrieTree struct { root *Node } // Add 把words[beginIndex:]插入到Trie樹中 func (n *Node) Add(worlds []rune, term string, beginIndex int) { // worlds遍歷完成,記錄關鍵詞 if beginIndex == len(worlds) { n.Term = term return } if n.Child == nil { n.Child = make(map[rune]*Node) } word := worlds[beginIndex] if child, ok := n.Child[word]; !ok { // 子節點不存在word,把word放到node子節點中 newNode := &Node{World: word} n.Child[word] = newNode // 遞迴遍歷 newNode.Add(worlds, term, beginIndex+1) } else { // 遞迴遍歷 child.Add(worlds, term, beginIndex+1) } } // Walk worlds[0]當前節點上儲存的字元,按照words的指引順著樹往下走,最終返回words最後一個字元對應的節點 func (n *Node) Walk(worlds []rune, beginIndex int) *Node { if beginIndex == len(worlds)-1 { return n } beginIndex += 1 word := worlds[beginIndex] if child, ok := n.Child[word]; ok { return child.Walk(worlds, beginIndex) } else { return nil } } // TraverseTerms 拿到節點的term func (n *Node) TraverseTerms(terms *[]string) { if len(n.Term) > 0 { *terms = append(*terms, n.Term) } for _, child := range n.Child { child.TraverseTerms(terms) } } // AddTerm 新增詞 func (t *TrieTree) AddTerm(term string) { if len(term) <= 1 { return } words := []rune(term) if t.root == nil { t.root = new(Node) } t.root.Add(words, term, 0) } // Retrieve 檢索要查詢的詞 func (t *TrieTree) Retrieve(prefix string) []string { if t.root == nil || len(t.root.Child) == 0 { return nil } words := []rune(prefix) firstWord := words[0] if child, ok := t.root.Child[firstWord]; ok { end := child.Walk(words, 0) if end == nil { return nil } else { terms := make([]string, 0, 100) end.TraverseTerms(&terms) return terms } } else { return nil } } func main() { tree := new(TrieTree) tree.AddTerm("分散") tree.AddTerm("分散精力") tree.AddTerm("分散投資") tree.AddTerm("分散式") tree.AddTerm("工程") tree.AddTerm("工程師") terms := tree.Retrieve("分散") fmt.Println(terms) terms = tree.Retrieve("人工") fmt.Println(terms) } >>>>>> [分散 分散精力 分散投資] []