演算法--統計文字中出現次數最多的單詞(字典樹)
統計一個文字中,出現次數最多的單詞:單詞全部小寫,單詞與單詞之間以空格間隔
1.利用字典 key為單詞 value為單詞出現的次數
def mostString(): dict = {} fr = open('preprocessing.txt') k = 0 n = 0 for line in fr.readlines(): for s in line.strip().split(' '): if s not in dict.keys(): dict[s] = 0 else: dict[s] = dict[s] + 1 if dict[s]>=n: k = s n = dict[s] #print(dict) print(k)
程式碼如上,利用一個字典來儲存所有字典,value來儲存每個單詞的次數,當字典中沒有該單詞時,執行新增操作;若已經有該單詞則執行更新操作,最後返回次數最多的單詞即可。空間複雜度N 時間複雜度平均為N
2.字典樹解法
字典樹,又稱為單詞查詢樹,Tire數,是一種樹形結構,它是一種雜湊樹的變種。
基本性質如下:
- 根節點不包含字元,除根節點外的每一個子節點都包含一個字元
- 從根節點到某一節點。路徑上經過的字元連線起來,就是該節點對應的字串
- 每個節點的所有子節點包含的字元都不相同
典型應用是用於統計,排序和儲存大量的字串(不僅限於字串),經常被搜尋引擎系統用於文字詞頻統計。
利用字串的公共字首來減少查詢時間,最大限度的減少無謂的字串比較,查詢效率比雜湊樹高。
如果文字中的單詞數量很多,並且大部分單詞字首都相同,那麼利用第一種字典方式來儲存所有單詞,則會浪費許多空間,此時可以利用字典樹來儲存單詞,充分利用最大公共字首。
構造字典樹節點:
class TrieNode: def __init__(self, var=None, parent=None, num=0): self.num = num self.isEnd = False self.son = {} self.var = var self.parent = parent
num 該字元在某種字首中出現的次數
isEnd true標識某個單詞以該字元結尾
var 該節點儲存的字元
son 該節點所有葉子節點
parent 指向父節點的指標
1.建構函式:
root = None
def __init__(self):
self.root = TrieNode()
2.在字典樹中插入一個單詞:
def insert(self, str):
if len(str)<=0:
return
node = self.root
for c in str:
if c not in node.son.keys():
node.son[c] = TrieNode(c, node, 1)
else:
node.son[c].num += 1
node = node.son[c]
node.isEnd = True
遍歷單詞的每個字元,找到從根節點開始的一條路徑來儲存所有字元,若字元不存在則新建一個節點,若已經存在則更新計數,最後將單詞最後一個字元指示變數isEnd置為true
2.在字典樹中查詢一個單詞是否存在
def has(self, str):
if len(str)==0:
return False
node = self.root
for c in str:
if c not in node.son.keys():
return False
else:
node = node.son[c]
return node.isEnd
從字典樹的根節點開始,依次匹配單詞的每個字元,若最終存在某條路徑包含該單詞所有字元,則觀察最後一個字元的指示變數
3.計算以某個字元為字首的所有單詞總數
def countPrefix(self, prefix):
if len(prefix)==0:
return -1
node = self.root
for c in prefix:
if c not in node.son.keys():
return 0
else:
node = node.son[c]
return node.num
找到該字串的最後一個字元位置,返回計數值即可。
4.遍歷字典樹
def preOrder(self, node):
if node != None:
print (list(node.son.keys()))
for child in node.son.keys():
self.preOrder(node.son[child])
5.計算出現次數最多的單詞
def mostString(self):
max = [0]
r = [TrieNode()]
self.helper(self.root, max, r)
x = r[0]
print(x)
s = []
while x!=None:
s.append(x.var)
x = x.parent
s.reverse()
return s
def helper(self, node, max, r):
if node != None:
print (node.num, node.isEnd)
if node.isEnd and node.num >= max[0]:
r[0] = node
max[0] = node.num
print('r value ', max, r[0].var)
for child in node.son.keys():
self.helper(node.son[child], max, r)
如上所示,遍歷每一個節點,找到那些指示變數isEnd==true並且計數值最大的節點,從該節點沿parent指標回溯到根節點,然後倒序輸出即可。
def mostString2():
dict = {}
t = Trie()
fr = open('preprocessing.txt')
for line in fr.readlines():
for s in line.strip().split(' '):
print(s)
t.insert(s)
#print(t.preOrder(t.root))
#print(t)
#t.preOrder(t.root)
#print(k)
print(t.has('chen'))
print(t.has('chendsfdsfsd'))
print(t.countPrefix('chen'))
print(t.mostString())
問題:
1.python中是否支援物件陣列,本來想利用物件陣列來儲存某節點的孩子節點集合的
2.python中引數傳遞問題,為求最大值,函式傳遞時使用max=[0] r=[TrieNode()],很彆扭
max=0屬於不可變物件可以理解,但是r=TrieNode() r應該是可變物件吧,但是這裡好像也是不可改變的
測試一下:
def canchange(r):
r = TrieNode(2)
print('func inner ',r.var)
def mostString2():
dict = {}
t = Trie()
fr = open('preprocessing.txt')
for line in fr.readlines():
for s in line.strip().split(' '):
print(s)
t.insert(s)
#print(t.preOrder(t.root))
#print(t)
#t.preOrder(t.root)
#print(k)
print(t.has('chen'))
print(t.has('chendsfdsfsd'))
print(t.countPrefix('chen'))
print(t.mostString())
r = TrieNode(1)
print('before',r.var)
canchange(r)
print('after',r.var)
可知物件指標是不可變物件,函式傳遞屬於值傳遞,好奇怪!
參考地址: