python 字典樹(前綴樹)基本操作:插入,刪除、查找


python實現字典樹

前言

  trie 樹 也叫字典樹,也是一種 N 叉樹,是一種特殊的前綴樹結構。通常來說,一個前綴樹是用來存儲字符串的。前綴樹的每一個節點代表一個字符串(前綴)。每一個節點會有多個子節點,通往不同子節點的路徑上有着不同的字符。子節點代表的字符串是由節點本身的原始字符串,以及通往該子節點路徑上所有的字符組成的。

  前綴樹的一個重要的特性是,節點所有的后代都與該節點相關的字符串有着共同的前綴。這就是前綴樹名稱的由來。

 

實現

  1 # coding=utf-8
  2 #字典嵌套牛逼,別人寫的,這樣每一層非常多的東西,搜索就快了,樹高26.所以整體搜索一個不關多大的單詞表,還是O(1).
  3 
  4 '''
  5     Python 字典 setdefault() 函數和get() 方法類似, 如果鍵不存在於字典中,將會添加鍵並將值設為默認值。
  6     說清楚就是:如果這個鍵存在字典中,那么這句話就不起作用,否則就添加字典里面這個key的取值為后面的默認值.
  7     簡化了字典計數的代碼.並且這個函數的返回值是做完這些事情之后這個key的value值.
  8     dict.setdefault(key, default=None)
  9     Python 字典 get() 函數返回指定鍵的值,如果值不在字典中返回默認值。
 10     dict.get(key, default=None)
 11 '''
 12 
 13 
 14 class Trie:
 15     root = {}
 16     END = '/'  # 加入這個是為了區分單詞和前綴,如果這一層node里面沒有/他就是前綴.不是我們要找的單詞.
 17 
 18     def insert(self, word):
 19         # 從根節點遍歷單詞,char by char,如果不存在則新增,最后加上一個單詞結束標志
 20         node = self.root
 21         for c in word:
 22             """
 23             利用嵌套來做,一個trie樹的子樹也是一個trie樹.
 24             利用setdefault的返回值是value的特性,如果找到了key就進入value
 25             沒找到,就建立一個空字典
 26             """
 27             node = node.setdefault(c, {})
 28         node[self.END] = None
 29         # 當word都跑完了,就已經沒有字了.那么當前節點也就是最后一個字母的節點
 30         # 加一個屬性標簽end.這個end里面隨意放一個value即可.因為我們只是判定end這個key是否在字典里面.
 31         # 考慮insert 同一個單詞2次的情況,第二次insert 這個單詞的時候,因為用setdefault
 32         # insert里面的話都不對原字典進行修改.正好是我們需要的效果.
 33         # 這個self.END很重要,可以作為信息來存儲.比如里面可以輸入這個單詞的
 34         # 起源,發音,拼寫,詞組等作為信息存進去.找這個單詞然后讀出單詞的信息.
 35 
 36     def delete(self, word):  # 字典中刪除word
 37         node = self.root
 38         for c in word:
 39             if c not in node:
 40                 print('字典中沒有不用刪')
 41                 return False
 42             node = node[c]
 43         # 如果找到了就把'/'刪了
 44         del node['/']
 45         # 后面還需要檢索一遍,找一下是否有前綴的后面沒有單詞的.把前綴的最后一個字母也去掉.因為沒單詞了,前綴也沒意義存在了.
 46         # 也就是說最后一個字母這個節點,只有'/',刪完如果是空的就把這個節點也刪了.
 47         while node == {}:
 48             if word == '':
 49                 return
 50             tmp = word[-1]
 51             word = word[:-1]
 52             node = self.root
 53             for c in word:
 54                 node = node[c]
 55             del node[tmp]
 56 
 57     def search(self, word):
 58         node = self.root
 59         for c in word:
 60             if c not in node:
 61                 return False
 62             node = node[c]
 63         return self.END in node
 64 
 65     def associate_search(self, pre):  # 搜索引擎里面的功能是你輸入東西,不關是不是單詞,他都輸出以這個東西為前綴的單詞.
 66         node = self.root
 67         for c in pre:
 68             if c not in node:
 69                 return []  # 因為字典里面沒有pre這個前綴
 70             node = node[c]  # 有這個前綴就繼續走,這里有個問題就是需要記錄走過的路徑才行.
 71         # 運行到這里node就是最后一個字母所表示的字典.
 72         # 舉一個栗子:圖形就是{a,b,c}里面a的value是{b,c,d} d的value是{/,e,f} 那么/代表的單詞就是ad,看這個形象多了
 73         # 首先看這個字母所在的字典有沒有END,返回a這個list
 74 
 75         # 然后下面就是把前綴是pre的單詞都加到a里面.
 76         # 應該用廣度遍歷,深度遍歷重復計算太多了.好像深度也很方便,並且空間開銷很小.
 77         # 廣度不行,每一次存入node,沒用的信息存入太多了.需要的信息只是這些key是什么,而不需要存入node.
 78         # 但是深度遍歷,又需要一個flag記錄每個字母.字典的key又實現不了.
 79         # 用函數遞歸來遍歷:只能先用這個效率最慢的先寫了
 80         # 因為你遍歷一直到底,到底一定是'/'和None.所以一定travel出來的是單詞不是中間結果.
 81         def travel(node):  # 返回node節點和他子節點拼出的所有單詞
 82             if node == None:
 83                 return ['']
 84             a = []  # 現在node是/ef
 85 
 86             for i in node:
 87                 tmp = node[i]
 88                 tmp2 = travel(tmp)
 89                 for j in tmp2:
 90 
 91                     a.append(i + j)
 92             return a
 93 
 94         output = travel(node)
 95         for i in range(len(output)):
 96             output[i] = (pre + output[i])[:-1]
 97         return output
 98 
 99 
100 
101 a = Trie()
102 a.insert('apple')
103 a.insert('appl')
104 a.insert("badj")
105 print(a.root)
106 print(a.associate_search('ap'))
107 a.delete('apple')
108 print(a.search('apple'))

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM