【譯】Swift算法俱樂部-字典樹

本文是對 Swift Algorithm Club 翻譯的一篇文章。 Swift Algorithm Clubraywenderlich.com網站出品的用Swift實現算法和數據結構的開源項目,目前在GitHub上有18000+⭐️,我初略統計了一下,大概有一百左右個的算法和數據結構,基本上常見的都包含了,是iOSer學習算法和數據結構不錯的資源。 🐙andyRon/swift-algorithm-club-cn是我對Swift Algorithm Club,邊學習邊翻譯的項目。因爲能力有限,如發現錯誤或翻譯不妥,請指正,歡迎pull request。也歡迎有興趣、有時間的小夥伴一塊兒參與翻譯和學習🤓。固然也歡迎加⭐️,🤩🤩🤩🤨🤪。 本文的翻譯原文和代碼能夠查看🐙swift-algorithm-club-cn/Triegit


字典樹(Trie)github

這個話題已經有個輔導文章算法

什麼是字典樹

Trie(在一些其餘實現中也稱爲前綴樹或基數樹)是用於存儲關聯數據結構的特殊類型的樹。 Trie做爲一個字典可能以下所示:swift

A Trie

存儲英語是Trie的主要用處。 Trie中的每一個節點都表明一個單詞的單個字符。 而後,一系列節點組成一個單詞。數據結構

爲何須要字典樹?

字典樹對某些狀況很是有用。 如下是一些優勢:學習

  • 查找值一般具備更好的最壞狀況時間複雜度。
  • 與哈希映射不一樣,Trie不須要擔憂鍵衝突。
  • 不使用散列來保證元素的惟一路徑。
  • Trie結構默認按字母順序排列。

經常使用算法

包含(或任何常規查找方法)

Trie結構很是適合查找操做。 對於模擬英語語言的Trie結構,找到一個特定的單詞就是幾個指針遍歷的問題:網站

func contains(word: String) -> Bool {
	guard !word.isEmpty else { return false }

	// 1
	var currentNode = root
  
	// 2
	var characters = Array(word.lowercased().characters)
	var currentIndex = 0
 
	// 3
	while currentIndex < characters.count, 
	  let child = currentNode.children[characters[currentIndex]] {

	  currentNode = child
	  currentIndex += 1
	}

	// 4
	if currentIndex == characters.count && currentNode.isTerminating {
	  return true
	} else {
	  return false
	}
}
複製代碼

contains方法至關簡單:ui

  1. 建立對root的引用。 此引用將容許您沿着節點鏈向下走。
  2. 跟蹤你想要匹配的單詞的字符。
  3. 將指針向下移動節點。
  4. isTerminating是一個布爾標誌,表示該節點是不是單詞的結尾。 若是知足此if條件,則意味着您能夠在trie中找到該單詞。

插入

插入Trie須要您遍歷節點,直到您中止必須標記爲terminating的節點,或者到達須要添加額外節點的點。spa

func insert(word: String) {
  guard !word.isEmpty else {
    return
  }

  // 1
  var currentNode = root

  // 2
  for character in word.lowercased().characters {
    // 3
    if let childNode = currentNode.children[character] {
      currentNode = childNode
    } else {
      currentNode.add(value: character)
      currentNode = currentNode.children[character]!
    }
  }
  // Word already present?
  guard !currentNode.isTerminating else {
    return
  }

  // 4
  wordCount += 1
  currentNode.isTerminating = true
}
複製代碼
  1. 再次,您建立對根節點的引用。 您將此引用沿着節點鏈移動。
  2. 逐字逐句地逐字逐句
  3. 有時,要插入的節點已存在。 這是Trie裏面兩個共享字母的詞(即「Apple」,「App」)。若是一個字母已經存在,你將重複使用它,並簡單地遍歷鏈條。 不然,您將建立一個表示該字母的新節點。
  4. 一旦結束,將isTerminating標記爲true,將該特定節點標記爲單詞的結尾。

刪除

從字典樹中刪除鍵有點棘手,由於還有一些狀況須要考慮。 Trie中的節點能夠在不一樣的單詞之間共享。 考慮兩個詞「Apple」和「App」。 在Trie中,表明「App」的節點鏈與「Apple」共享。翻譯

若是你想刪除「Apple」,你須要注意保持「App」鏈。

func remove(word: String) {
  guard !word.isEmpty else {
    return
  }

  // 1
  guard let terminalNode = findTerminalNodeOf(word: word) else {
    return
  }

  // 2
  if terminalNode.isLeaf {
    deleteNodesForWordEndingWith(terminalNode: terminalNode)
  } else {
    terminalNode.isTerminating = false
  }
  wordCount -= 1
}
複製代碼
  1. findTerminalNodeOf遍歷字典樹,找到表明word的最後一個節點。 若是它沒法遍歷字符串,則返回nil
  2. deleteNodesForWordEndingWith遍歷後綴,刪除word表示的節點。

時間複雜度

設n是Trie中某個值的長度。

  • contains - 最差狀況O(n)
  • insert - O(n)
  • remove - O(n)

其餘值得注意的操做

  • count:返回Trie中的鍵數 —— O(1)
  • words:返回包含Trie中全部鍵的列表 —— O(1)
  • isEmpty:若是Trie爲空則返回true,不然返回false —— O(1)

擴展閱讀字典樹的維基百科

做者:Christian Encarnacion, Kelvin Lau
翻譯:Andy Ron 校對:Andy Ron

相關文章
相關標籤/搜索