本文主要做爲本身的學習筆記,並不具有過多的指導意義。
前綴樹又名字典樹,單詞查找樹,Trie樹,是一種多路樹形結構,是哈希樹的變種,和hash效率有一拼,是一種用於快速檢索的多叉樹結構。多用於詞頻搜索或者模糊查詢。node
查詢時只與單樣本長度有關,而與樣本量無關。數組
給出一組單詞,inn, int, at, age, adv,ant, 咱們能夠獲得下面的Trie:bash
如此,在進行依次輸入進行查詢時。只須要順着以前的樹繼續查詢便可,而不須要每次修改字符串都遍歷全部信息。app
在刪除了字符時,也只須要回滾到上層便可。學習
一個多叉樹結構的節點。前綴樹的功能,取決於節點的健壯性。ui
//前綴樹節點
public class TrieNode {
public var end :Bool //是否爲某個單詞最後節點,查詢用
public var path :Int //記錄有幾個字符串通過了這個節點,刪除用
public var nexts:Dictionary<String, TrieNode> //子樹路徑,基本功能
public init() {
self.end = false
self.nexts = Dictionary.init()
self.path = 0
}
}
複製代碼
其中子樹路徑,若是隻存儲字符串的話。當樣本量太大能夠乾脆使用數組[26]
的形式存儲。spa
持有一個根節點,具有增刪改查的功能.net
//前綴樹
public class Trie {
private var root:TrieNode
public init() {
self.root = TrieNode.init()
}
/// 添加字符串
public func insert(word:String) {
...
}
//查找
public func search(word:String) -> Bool {
...
}
//刪除
public func delete(word:String) {
...
}
}
複製代碼
將字符串按字母分割,從根節點依次追加進前綴樹code
須要注意若是樹中已經存在該節點路徑,則複用cdn
/// 添加字符串
public func insert(word:String) {
if word == nil {
return
}
let strs = wordToArr(word: word)
if strs.count == 0 {
return
}
//從根節點開始創建前綴樹
var node = root
for i in 0..<strs.count {
if node.nexts[strs[i]] == nil {
node.nexts.updateValue(TrieNode.init(), forKey: strs[i]) //沒有可服用路徑,新建節點
}
node = node.nexts[strs[i]]! //node跳到下層
node.path+=1 // 將當前節點計數+1
}
node.end = true //標記最後一個節點
}
複製代碼
查找與插入相似,但須要判斷最後一個節點的end屬性是否被標記
//查找
public func search(word:String) -> Bool {
if word == nil {
return false
}
let strs = wordToArr(word: word)
if strs.count == 0 {
return false
}
//從根節點開始創建前綴樹
var node = root
for i in 0..<strs.count {
if node.nexts[strs[i]] == nil {
return false //任何一步有空,說明未被插入過
}
node = node.nexts[strs[i]]!
}
return node.end //返回最後一個節點的標記狀態
}
複製代碼
先相似查找的方式肯定是否存在該數據,而後嘗試刪除。
每次刪除,實際上是將節點的path屬性-1。當path屬性==0時,從上級節點的nexts列表中remove掉該節點便可return結束。
//刪除
public func delete(word:String) {
if word == nil {
return
}
let strs = wordToArr(word: word)
if strs.count == 0 {
return
}
//嘗試查找指定字符串,而且保存在nodes備用
var node = root
var nodes : [TrieNode] = Array.init()
nodes.append(node)
for i in 0..<strs.count {
if node.nexts[strs[i]] == nil {
return
}
node = node.nexts[strs[i]]!
nodes.append(node) //將查詢到的節點放入數組備用
}
//若是最後一個節點不是end,說明不存在該字符串
if node.end == true{
//遍歷nodes數組,當某個節點path==1說明能夠直接刪除該節點了
for i in 1..<nodes.count { //從1開始,0是root節點
if nodes[i].path == 1 {
nodes[i-1].nexts.removeValue(forKey: strs[i-1])
return
}
nodes[i].path -= 1
}
}
}
複製代碼
相對於前綴樹來講,後綴樹並非針對大量字符串的,而是針對一個或幾個字符串來解決問題。好比字符串的迴文子串,兩個字符串的最長公共子串等等。
好比blanana
這個單詞,在後綴樹的結構中將以以下結構展示(爲了方便看到後綴,我沒有合併相同的前綴)
an
是否在banana
中 如上圖所致,a
+n
的節點順序已經存在於後綴樹中,因此返回true
a
+n
在banana
中重複的次數。 如上圖所致,a
+n
中n
節點的path
計數爲2,因此重複次數爲2.