python Trie樹和雙數組TRIE樹的實現. 擁有3個功能:插入,刪除,給前綴智能找到所有能匹配的單詞


#coding=utf-8  #字典嵌套牛逼,別人寫的,這樣每一層非常多的東西,搜索就快了,樹高26.所以整體搜索一個不關多大的單詞表
#還是O(1).
'''
Python 字典 setdefault() 函數和get() 方法類似, 如果鍵不存在於字典中,將會添加鍵並將值設為默認值。
說清楚就是:如果這個鍵存在字典中,那么這句話就不起作用,否則就添加字典里面這個key的取值為后面的默認值.
簡化了字典計數的代碼.並且這個函數的返回值是做完這些事情之后這個key的value值.
dict.setdefault(key, default=None)
Python 字典 get() 函數返回指定鍵的值,如果值不在字典中返回默認值。
dict.get(key, default=None)
'''
class Trie:  
    root = {}  
    END = '/'  #加入這個是為了區分單詞和前綴,如果這一層node里面沒有/他就是前綴.不是我們要找的單詞.
    def add(self, word):  
        #從根節點遍歷單詞,char by char,如果不存在則新增,最后加上一個單詞結束標志  
        node = self.root  
        for c in word:  
            node=node.setdefault(c,{})  #利用嵌套來做,一個trie樹的子樹也是一個trie樹.
                                        #利用setdefault的返回值是value的特性,如果找到了key就進入value
                                        #沒找到,就建立一個空字典然后
        node[self.END] = None          #當word都跑完了,就已經沒有字了.那么當前節點也就是最后一個字母的節點
                                        #加一個屬性標簽end.這個end里面隨意放一個value即可.因為我們判定只是
                                        #判定end這個key是否在字典里面.
                                        #考慮add 同一個單詞2次的情況,第二次add 這個單詞的時候,因為用setdefault
                                        #add里面的話都不對原字典進行修改.正好是我們需要的效果.
                                        
                                        #這個self.END很重要,可以作為信息來存儲.比如里面可以輸入這個單詞的
                                        #起源,發音,拼寫,詞組等作為信息存進去.找這個單詞然后讀出單詞的信息.
  
    def find(self, word):  
        node = self.root  
        for c in word:  
            if c not in node:  
                return False  
            node = node[c]  
        return self.END in node  
    def associate_find(self, pre):  #搜索引擎里面的功能是你輸入東西,不關是不是單詞,他都輸出以這個東西為前綴
                                       #的單詞.
        node = self.root  
        for c in pre:  
            if c not in node:  
                return []  #因為字典里面沒有pre這個前綴
            node = node[c]  #有這個前綴就繼續走,這里有個問題就是需要記錄走過的路徑才行.
        #運行到這里node就是最后一個字母所表示的字典.
#舉一個栗子:圖形就是{a,b,c}里面a的value是{b,c,d} d的value是{/,e,f} 那么/代表的單詞就是ad,看這個形象多了
        #首先看這個字母所在的字典有沒有END,返回a這個list

        

        #然后下面就是把前綴是pre的單詞都加到a里面.
        #應該用廣度遍歷,深度遍歷重復計算太多了.好像深度也很方便,並且空間開銷很小.
        #廣度不行,每一次存入node,沒用的信息存入太多了.需要的信息只是這些key是什么,而不需要存入node.
        #但是深度遍歷,又需要一個flag記錄每個字母.字典的key又實現不了.
        #用函數遞歸來遍歷:只能先用這個效率最慢的先寫了
        #因為你遍歷一直到底,到底一定是'/'和None.所以一定bianli出來的是單詞不是中間結果.
        def bianli(node):#返回node節點和他子節點拼出的所有單詞
            if node==None:
                return ['']
            a=[]#現在node是/ef
            
            for i in node:
                tmp=node[i]
                tmp2=bianli(tmp)
                for j in tmp2:

                  a.append(i+j)
            return a
        output=bianli(node)
        for i in range(len(output)):
            output[i]=(pre+output[i])[:-1]
        return output











    def delete(self, word):#字典中刪除word
        node = self.root  
        for c in word:  
            if c not in node:  
                print('字典中沒有不用刪')
                return False  
            node = node[c]  
        #如果找到了就把'/'給他刪了就行了
        del node['/']  
        #后面還需要檢索一遍,找一下是否有前綴的后面沒有單詞的.把前綴的最后一個字母也去掉.因為沒單詞了,前綴也沒意義存在了.
        #也就是說最后一個字母這個節點,只有'/',刪完如果是空的就把這個節點也刪了.
        while node=={}:
            if word=='':
                return 
            tmp=word[-1]
            word=word[:-1]
            node = self.root  
            for c in word:  
              node = node[c]
            del node[tmp]


a=Trie()

(Trie.END)#python這個也是吊,類方法和類屬性:自動也是對象的方法或者屬性!
a.add('apple')
a.add('appl')
a.delete('apple')

print(a.find('apple'))
print(a.root)#發現完美的解決了刪除功能.刪除apple因為沒有其他單詞了就把整個字典刪了
#下面我打算加一個功能就是詞匯聯想功能,輸入a,輸出a,ab,abc.就是把a后面的字典里面的所有的單詞就輸出出來.


#兩個字典的key相同,id就相同.真坑.用id區分不了2個取值相同的不同元素.
#my={'a':{}}
#print(type(my))
#my['a']={'a':{'/'}}
#for i in my:
#   print(id(i))
#   a=my[i]
#   for j in a:
#       print(id(j))
View Code

把問題寫下來:

對於插入刪除還是挺滿意的,就是前綴這個功能效率貌似太低了.因為是函數迭代所以會產生大量的重復計算.但是字典里面又不能隨機訪問.id對於重復字母會沖突.flag也不好弄.

想到的唯一方法就是建立一個class node.把數據放到node里面.然后node里面再裝入一個字典.就是把字典封裝一下,帶一個flag功能(或者直接手動給一個id計數).來記錄所有記錄過的值,建立一個memo字典來避免重復計算.

繼續想法還有:智能聯想,根據單詞出現的頻率來輸出數據.  任意片段聯想,寫單詞中間的一個片段來聯想所有能匹配的.還沒想到太好的方法.這個任意片段,如果bianli所有字典就感覺太慢了.但是不這么做又會感覺結果不全.先做這么多,后面還有  

雙數組Trie樹(DoubleArrayTrie)  需要實現.

聽9章算數直通bat課程的老師說騰訊考過雙數組trie樹.

https://www.cnblogs.com/studyhs/p/5443767.html

太牛逼的東西完全沒看懂.

只看懂,他本質是一個4*n的數組,通過里面的整數進行下表索引.就能很快找到單詞.類似字典查詢.

只需要把單個中文字進行編碼.然后通過編碼和這個數組進行查詢就能找到任意詞匯所在的位置.然后可以再建立一個數組存信息即刻.

 

#coding=utf-8  #字典嵌套牛逼,別人寫的,這樣每一層非常多的東西,搜索就快了,樹高26.所以整體搜索一個不關多大的單詞表
#還是O(1).
'''
Python 字典 setdefault() 函數和get() 方法類似, 如果鍵不存在於字典中,將會添加鍵並將值設為默認值。
說清楚就是:如果這個鍵存在字典中,那么這句話就不起作用,否則就添加字典里面這個key的取值為后面的默認值.
簡化了字典計數的代碼.並且這個函數的返回值是做完這些事情之后這個key的value值.
dict.setdefault(key, default=None)
Python 字典 get() 函數返回指定鍵的值,如果值不在字典中返回默認值。
dict.get(key, default=None)
'''
class Trie:  
    root = {}  
    END = '/'  #加入這個是為了區分單詞和前綴,如果這一層node里面沒有/他就是前綴.不是我們要找的單詞.
    def add(self, word):  
        #從根節點遍歷單詞,char by char,如果不存在則新增,最后加上一個單詞結束標志  
        node = self.root  
        for c in word:  
            node=node.setdefault(c,{})  #利用嵌套來做,一個trie樹的子樹也是一個trie樹.
                                        #利用setdefault的返回值是value的特性,如果找到了key就進入value
                                        #沒找到,就建立一個空字典然后
        node[self.END] = None          #當word都跑完了,就已經沒有字了.那么當前節點也就是最后一個字母的節點
                                        #加一個屬性標簽end.這個end里面隨意放一個value即可.因為我們判定只是
                                        #判定end這個key是否在字典里面.
                                        #考慮add 同一個單詞2次的情況,第二次add 這個單詞的時候,因為用setdefault
                                        #add里面的話都不對原字典進行修改.正好是我們需要的效果.
                                        
                                        #這個self.END很重要,可以作為信息來存儲.比如里面可以輸入這個單詞的
                                        #起源,發音,拼寫,詞組等作為信息存進去.找這個單詞然后讀出單詞的信息.
  
    def find(self, word):  
        node = self.root  
        for c in word:  
            if c not in node:  
                return False  
            node = node[c]  
        return self.END in node  
    def associate_find(self, pre):  #搜索引擎里面的功能是你輸入東西,不關是不是單詞,他都輸出以這個東西為前綴
                                       #的單詞.
        node = self.root  
        for c in pre:  
            if c not in node:  
                return []  #因為字典里面沒有pre這個前綴
            node = node[c]  #有這個前綴就繼續走,這里有個問題就是需要記錄走過的路徑才行.
        #運行到這里node就是最后一個字母所表示的字典.
#舉一個栗子:圖形就是{a,b,c}里面a的value是{b,c,d} d的value是{/,e,f} 那么/代表的單詞就是ad,看這個形象多了
        #首先看這個字母所在的字典有沒有END,返回a這個list

        

        #然后下面就是把前綴是pre的單詞都加到a里面.
        #應該用廣度遍歷,深度遍歷重復計算太多了.好像深度也很方便,並且空間開銷很小.
        #廣度不行,每一次存入node,沒用的信息存入太多了.需要的信息只是這些key是什么,而不需要存入node.
        #但是深度遍歷,又需要一個flag記錄每個字母.字典的key又實現不了.
        #用函數遞歸來遍歷:只能先用這個效率最慢的先寫了
        #因為你遍歷一直到底,到底一定是'/'和None.所以一定bianli出來的是單詞不是中間結果.
        def bianli(node):#返回node節點和他子節點拼出的所有單詞
            if node==None:
                return ['']
            a=[]#現在node是/ef
            
            for i in node:
                tmp=node[i]
                tmp2=bianli(tmp)
                for j in tmp2:

                  a.append(i+j)
            return a
        output=bianli(node)
        for i in range(len(output)):
            output[i]=(pre+output[i])[:-1]
        return output











    def delete(self, word):#字典中刪除word
        node = self.root  
        for c in word:  
            if c not in node:  
                print('字典中沒有不用刪')
                return False  
            node = node[c]  
        #如果找到了就把'/'給他刪了就行了
        del node['/']  
        #后面還需要檢索一遍,找一下是否有前綴的后面沒有單詞的.把前綴的最后一個字母也去掉.因為沒單詞了,前綴也沒意義存在了.
        #也就是說最后一個字母這個節點,只有'/',刪完如果是空的就把這個節點也刪了.
        while node=={}:
            if word=='':
                return 
            tmp=word[-1]
            word=word[:-1]
            node = self.root  
            for c in word:  
              node = node[c]
            del node[tmp]


a=Trie()

(Trie.END)#python這個也是吊,類方法和類屬性:自動也是對象的方法或者屬性!
a.add('我是')
a.add('我是草擬嗎')


print(a.find('我是'))
print(a.root)#發現完美的解決了刪除功能.刪除apple因為沒有其他單詞了就把整個字典刪了
#下面我打算加一個功能就是詞匯聯想功能,輸入a,輸出a,ab,abc.就是把a后面的字典里面的所有的單詞就輸出出來.


#兩個字典的key相同,id就相同.真坑.用id區分不了2個取值相同的不同元素.
#my={'a':{}}
#print(type(my))
#my['a']={'a':{'/'}}
#for i in my:
#   print(id(i))
#   a=my[i]
#   for j in a:
#       print(id(j))

#下面分析為什么中文要用雙數組trie樹,英文用trie樹,英文里面每一個小dic至多27個數據.也就是26個字母加上'/'
'''
這樣如果假設每一個字母組合都是單詞的話,空間是26**len(word).假設word長度常用為8.但是事實上也就是
12**8 差不多了.結果4億個字母.換算空間的話.就400M空間而已.

下面分析中文:常用字2千個,每個字組詞匹配算15個基本夠了,詞組長度基本不超過4個.才一億個漢字,常用的轉碼是一個
漢字2個字節.所以才2億個字節.也就200M空間.但是為什么說trie樹存漢字詞組空間不夠?


https://segmentfault.com/a/1190000008877595
這個鏈接完美解決了上面問題
'''
View Code

原來是hash的效率對於大數據還是不夠快.比O(1)實際上要慢很多.空間也消耗大.

不說廢話了:總之:一定要弄好雙數組樹.他真正使用的方法是查詢.這個絕對是O(1)了.簡直快的不行!實際應用都是把數據直接學習完畢,不會插入和刪除太多.所以

雙數組trie樹是最好的選擇:他在分詞和機器學習里面對字符處理超重要.

 

 

'''
https://segmentfault.com/a/1190000008877595#articleHeader7
5.4.2 Base Array 的構造
看這里面寫的還真不難,之前一直沒看懂,是因為他數據沒有顯示寫入.
其實還有一個數組用來寫入數據.比如
這里面第一步之后的data數組變成了
data[2]=''
data[3]=''
data[7]=''
這樣通過他的步驟,做到最后就是3個數組,data,base,check3個數組來
表示這個2array trie.就能方便找到每一個詞組了.
'''
View Code

 嘔心瀝血之作 DAT 雙數組trie樹

'''

ps:#a=[i*i for i in range(5) if i<3 ]  #python for if 的一行寫法.
https://segmentfault.com/a/1190000008877595#articleHeader7
5.4.2 Base Array 的構造
看這里面寫的還真不難,之前一直沒看懂,是因為他數據沒有顯示寫入.
其實還有一個數組用來寫入數據.比如
這里面第一步之后的data數組變成了
data[2]=''
data[3]=''
data[7]=''
這樣通過他的步驟,做到最后就是3個數組,data,base,check3個數組來
表示這個2array trie.就能方便找到每一個詞組了.
但是寫起來簡直吐血.

首先看最終得到的結果如何使用它來找到所有的詞組:

字典:''清華”、“清華大學”、“清新”、“中華”、“華人”
編碼:清-1,華-2,大-3,學-4,新-5,中-6,人-7

數組下表:0    1   2   3   4   5   6   7   8   9   10
base數組:13   2   2   3   6   2   3   2    6

1.使用:找清華:首先從base[0]出發.清在的位置是base[0]+code(清)=下表為2的地方
           清的base數組不是負數,說明有繼續拓展的本事.所以找下一個詞華可以找.
           華=他上一個節點的base值+code(華)=3+2=5.所以就找到了清華在我們字典里面存在
       找清華大學:上面華找到了,繼續找大=base(華)+code(大)=5(注意是清華的華,所以是上面找到的3)+3=6
                  繼續找學=base[6]+code(學)=10.所以清華大學找到了.
  繼續細化:葉子節點的處理:將詞的最后一個節點的轉移基數統一改為某個負數
           所以 數組下表:0    1   2   3   4   5    6    7    8    9   10
                base數組:13   2   -2   -3   6   2   -3   -2    -6
          這樣做的代價就是需要將狀態轉移函數base[s]+code(字符)改為|base[s]|+code(字符)
          重新跑一次清華:上來還是清=1+1=2   華=3+2=5  然后看base[5]=-3 ,所以可以到此結束來組成一個詞匯.
          但是我們還可以繼續跑
          來找清華大學:從華找大:大=|-3|+code(大)=6,base[6]不是負數,不能輸出.
                      繼續找學:學=6+4=10,他的base是-6.所以可以輸出.
  加入check數組來解決bug:比如找'清中':找清我們到了3,找中我們到了9.base[9]=-2.所以我們輸出'清中'是一個詞匯.
                        這顯然是錯誤的!所以我們要加入check數組來避免這種匹配.這種bug的原因是中這個詞前面
                        不能是清這個字.用check數組來記錄這個位置前面一個字符所在的index.
          所以 數組下表:0    1   2   3   4   5    6    7    8    9   10
               base數組:13   2   -2   -3   6   2   -3   -2    -6
               check  :-3   -1   0   0   7   2     5   0   2    3     6
               這樣找清中:清是到了index2.判斷check是不是清的上一個節點.是0(0表示根)沒問題.
                         找中找到index9.然后需要判斷check[9]是不是他過來的節點的index.發現一個是2,一個是3
                         所以不對.輸出清中不存在.
2.搭建:
https://blog.csdn.net/kissmile/article/details/47417277
這個寫的也是不錯.但是他搭建的順序有一點錯誤,按照層搭建,第五部分應該是搭建第一層的b后面的c節點.
邏輯基本就是這樣,能講清楚就不錯了.基本達到智商110以上了.能代碼實現感覺智商上150了.
因為比較復雜,還是先寫偽代碼.再實現.
                          

題目:建立字典:字典:''清華”、“清華大學”、“清新”、“中華”、“華人”
偽代碼過程:
●a=[''清華”、“清華大學”、“清新”、“中華”、“華人”],b=sum([len(i) for i in a])
●對set(a)進行編碼:清-1,華-2,大-3,學-4,新-5,中-6,人-7
●建立首字集合c:清,中,華
●為了數組足夠長,建立base=[0]*b  check=[0]*b
●把c插入雙數組,對base[0]賦予初值1.(其實賦予2也一樣,貌似更好,因為初值1基本都會發生沖突,會降低建立速度)
 對新建立的base里面也放入1.
 把c插入后:
 數組下表:0    1   2   3   4   5   6   7   8   9   10
 base數組:1    0   1   1   0   0   0   1    0   0    0
 check  :0    0   0   0   0   0   0   0    0   0    0

●下面就是插入第二個字:華,新,華,人(第一個華,表示清后面的華,雖然他有2個但是前面都是清,所以只插入一個,這就是為什么
 Trie樹節省空間的原因).
 下面插入清后面的字:有華和新(對於同一個字的后面的字要一起考慮,因為可能要修改這同一個的base數組)
 從2開始跑,華=base[2]+code(華)=3.沖突了因為3里面已經有了.
 所以base[2]+=1.這時再算華=4了.不沖突了.
 插入新又沖突了.所以清要繼續加1.插入后的新元素base還是置1.(但是網上寫的是置清現在的base值.我感覺沒必要啊!!!!)
 也就是下圖5,8我都置1,但是網上置的是3.(下面通過我的計算,我置1最后還是為了解決沖突而加到3了.
 難道置3能減少沖突的發生?問題是會不會空間浪費太多?)(利用樹來看就是樹的第n層的偏移量一定比第n-1層的至少一樣或者多)
 (為什么?)(我認為是從概率上來講,每一個字符邊上的字符數量都一樣,所以你上個字母需要偏移3個才能不沖突,
 你也至少需要偏移3個.減少代碼運行時間.要知道處理沖突非常非常慢!!!!!)
 同時把check也更新了,也就是把清的index 2放進去.
 得到:
 
 數組下表:0    1   2   3   4   5   6   7   8   9   10
 base數組:1    0   3   1   0   1   0   1    1   0    0
 check  : 0    0   0   0   0   2   0   0    2   0    0
 
 
 (!!!!!!這里面就是遇到一個問題非常重要.搭建時候一定要多行一起搭建,也就是按照root的一層來搭建.把一層都弄好
 再弄下一層,原因就是我們最后需要得到的樹是一個公共前綴只保存一次的樹!也是問題的根本,不保持的話這個trie樹
 完全沒意義了,所以公共前綴保持同時處理,所以只能這樣按照root的層來搭建才可以.)
 同理插入中后面的字:7的base+=1.得到:
 數組下表:0    1   2   3   4   5   6   7   8   9   10
 base數組:1    0   3   1   1   1   0   2    1   0    0
 check  : 0    0   0   0   7   2   0   0    2   0    0

 同理華人:得到:
 數組下表:0    1   2   3   4   5   6   7   8   9   10
 base數組:1    0   3   2   1   1   0   2    1   1    0
 check  : 0    0   0   0   7   2   0   0    2   3    0


 第三層.
 得到:
 數組下表:0    1   2   3   4   5   6   7   8   9   10
 base數組:1    0   3   2   1   3   1   2    1   1    0
 check  : 0    0   0   0   7   2   5   0    2   3    0

  第四層.
 得到:
 數組下表:0    1   2   3   4   5   6   7   8   9   10
 base數組:1    0   3   2   1   3   6   2    1   1    1
 check  : 0    0   0   0   7   2   5   0    2   3    6



 總結:難度不比紅黑樹簡單.
'''
class DAT():
    def __init__(self,data):#通過這個函數返回self.base和self.check 2個數組
        #對data預處理:
        firststep=[]
        max_ceng=0#數據有多少層
        for i in data:
            a=0
            for j in i:
                firststep.append(j)
                a+=1
            if a>max_ceng:
                max_ceng=a
        all_len=len(firststep)
        mono_len=len(set(firststep))

        #用字典進行編碼.用數組太慢了,因為數組里面搜索是O(N)
        bianma={}
        ma=1
        tmp=[]
        for i in firststep:#這里面去重,為了測試先這么寫保順序,寫好后再改用set來加速
            if i not in tmp:
                tmp.append(i)
        for i in tmp:
            if i not in bianma:
               bianma[i]=ma
               ma+=1
        #我為了方便把''作為root,給他bianma 是0,然后base[0]=1
        bianma['']=0#只是為了遞歸寫起來代碼更簡潔而已.自我感覺很簡約.
        #初始化base 和check
        base=['#']*all_len  #雖然相同也不要用等號給check賦值base,因為list賦值是淺拷貝,傳的是地址
        base[0]=1
        check=['#']*all_len
        #打印一下編碼看看,因為字典是亂序的,每一次生成都不同,所以打印一下來驗算自己做的對不對.
        print(bianma)
        self.bianma=bianma
        #開始建立:
        #建立是按照第一列,...,最后一列這個順序進行遞歸的.
        #提取當前列的set后元素.
        #第一列可以看做''空字符開始的后面一個元素.
        #提取第一列:然后再遞歸修改成提取第i列
        

        before=''
        col_now=[i[len(before)] for i in data if before in i]#提取有before前綴的字符的下一個小字符.#第一層就是清,華,中
        tmp=[]
        for i in col_now:
            if i not in tmp:
                tmp.append(i)
        col_now=tmp
        print('第一列')
        print(col_now)
        #開始計算col_now里面的字符的base
        before_index=bianma[before]#其他層不是這么算的.
        now_layer_save_for_data=[]#為了下一層的遞推而記錄的文字信息
        now_layer_save_for_base=[]#為了下一層的遞推而記錄的index信息
        for i in col_now:
            
            while 1:
             index=base[before_index]+bianma[i]
             if base[index]=='#':#說明沒有人占用
                 base[index]=base[before_index]
                 check[index]=before_index
                 now_layer_save_for_data.append(i)
                 now_layer_save_for_base.append(index)
                 break
             else:
                 base[before_index]+=1
        last_layer=1
        print('第一層')
        print(base)#測試后第一層建立成功.
        print(check)
        print(max_ceng)
        print(now_layer_save_for_data)
        print(now_layer_save_for_base)
        #還是先寫遞推的寫法,遞歸的寫法想不清楚.
        #建立layer信息
        layer1={}
        for i in range(len(data)):
          for jj in range(len(now_layer_save_for_data)):
            j=now_layer_save_for_data[jj]
            j2=now_layer_save_for_base[jj]#光用漢字來做key會發生無法區分清華,中華這種bug.
            if data[i][0]==j:
                layer1.setdefault((j,j2),[])
                layer1[(j,j2)].append(i)
        #用layer1,data里面的信息,對base里面信息進行加工,也就是如果單字就取反
        for i in layer1:
            if i[0] in data:
                base[i[1]]=-base[i[1]]




        #搭建第二層:先找到將要被搭建的字
        #利用last_layer和now_layer_save_for_data和now_layer_save_for_base來找.
        now_layer=last_layer+1
        
        #for i in range(len(now_layer_save_for_data)):
        #    tmp=now_layer_save_for_data[i]#tmp就是清
        #    id=now_layer_save_for_base[i]#id 就是清的base數組里面的值
            #找到清后面的字,也就是data里面第一個字為清的字.如果每建立一個節點就遍歷一遍會是至少O(N方),並且
            #基本嚴格大於這個數字,太大了.我想法是一層的東西同時處理,這樣一層只遍歷一次.降到線性搜索.
            #對於同時一堆if,顯然效率不行,所以還是字典來替代多if並列.還是慢,想到用類似線段樹的手段來記錄.
            #里面的每一層用一個字典來表示,一個value是一個list
        #根據layer1建立layer2
        layer=layer1
        print(layer)
        #下面就可以建立layer2了#從這里就能分析出為什么要把上一層有同一個前綴的都建立完再弄下一個.
        #下面整合起來是從一個layer得到這個層的全base數組和check數組.可以封裝起來for循環.
        for iii in range(1,max_ceng):
            now_layer=iii+1
            layer4={}
            print(layer)  #layer1:{('', 2): [0, 1, 2], ('', 7): [3], ('', 3): [4]}
            
            for i in layer:
                lastword=i[0]
                lastindex=i[1]
                beixuan=layer[i]
                #找到應該插入哪個
                charu=[]
                #把beixuan里面長度不夠的剔除,他長度不夠其實就表示已經在上一步是詞組了.
                beixuan2=[]
                for i in beixuan :
                    if len(data[i])>=now_layer:
                        beixuan2.append(i)
                beixuan=beixuan2

                for i in beixuan:
                    newword=data[i][now_layer-1]
                    if newword not in charu:
                        charu.append(newword)
                #把charu里面的東西進入base,check算法中

                now_layer_save_for_data=[]#為了下一層的遞推而記錄的文字信息
                now_layer_save_for_base=[]#為了下一層的遞推而記錄的index信息
                col_now=charu #插入華,新
                before_index=abs(lastindex)
                for i in col_now:
            
                    while 1:
                     index=abs(base[before_index])+bianma[i]
                     if base[index]=='#':#說明沒有人占用

                         break
                     else:
                         if base[before_index]>0:
                          base[before_index]+=1
                         else:
                             base[before_index]-=1
                         print(base)
                #對於已經構成詞匯的詞語base里面的數要取相反數.
                beixuanciku=[data[i][now_layer-1:] for i in beixuan]
            #調試狀態vs2017把鼠標放變量上就能看到他的取值,很放方便.任意位置都能看
                for i in col_now:
                    if i in beixuanciku:
                        index=abs(base[before_index])+bianma[i]
                        base[index]=-abs(base[before_index])#注意這地方不能寫-要寫-abs
                        check[index]=before_index
                        now_layer_save_for_data.append(i)
                        now_layer_save_for_base.append(index)
                    else:
                        index=abs(base[before_index])+bianma[i]
                        base[index]=base[before_index]
                        check[index]=before_index
                        now_layer_save_for_data.append(i)
                        now_layer_save_for_base.append(index)
            

                #更新layer

                for i in beixuan:
                 for jj in range(len(now_layer_save_for_data)):
                    j=now_layer_save_for_data[jj]
                    j2=now_layer_save_for_base[jj]#光用漢字來做key會發生無法區分清華,中華這種bug.
                    if data[i][now_layer-1]==j:
                        layer4.setdefault((j,j2),[])
                        layer4[(j,j2)].append(i)


        #已經得到了新的layer4,替換回去,為了遞推.
            layer=layer4
             
            











            
        #打印上個layer
        print(layer)     #{('', 2): [0, 1, 2], ('', 7): [3], ('', 3): [4]} 上個layeer信息
        #下面需要更新layer
        layernew={}
        for i in layer:#逐個計算里面的對兒即可.比如先計算('', 2): [0, 1, 2]應該改成什么
          pass


          #for jj in range(len(now_layer_save_for_data)):
          #  j=now_layer_save_for_data[jj]
          #  j2=now_layer_save_for_base[jj]#光用漢字來做key會發生無法區分清華,中華這種bug.
          #  if data[i][0]==j:
          #      layer1.setdefault((j,j2),[])
          #      layer1[(j,j2)].append(i)







        print(now_layer_save_for_data)
        print(now_layer_save_for_base)



        print('測試')#第二列也zhengque 
        #經過我2天超過20個小時的學習和寫代碼,寫出了這個例子的base數組和check數組.修改些小bug就可以了.
        #絕逼不比紅黑樹簡單.網上也幾乎沒有代碼實現.因為我主題layer是從第一層建立后針對2到n層開始建立的
        #所以第一層如果是單字,直接返回這種情況,我還沒寫,但是相對蓋起來簡單.
        print(base)
        print(check)
        #最后的最后,用self把結果傳出去
        self.base=base
        self.check=check
                     
        





                 


        


    def search(self,a):#通過這個函數a在data是否存在,這個函數隨便玩了
        
        tmp=0
        #self寫起來太麻煩,
        bianma=self.bianma
        base=self.base
        check=self.check
        i=a[0]
        if len(a)==1:
            tmp=1+bianma[i]
            return base[tmp]<0
        else:
            first=1+bianma[a[0]]
            for i in range(len(a)-1):
                tmp=abs(base[first])+bianma[a[i+1]]
                if check[tmp]!=first:
                    return False
                first=tmp
            return base[tmp]<0
        
'''
base:[1, '#', -3, 2, -2, -3, -6, 2, -3, -2, -6, '#', '#']
check:['#', '#', 0, 0, 7, 2, 5, 0, 2, 3, 6, '#', '#']
'''





#測試:
a=DAT(['清華','清華大學','清新','中華','華人',''])
#進行search測試
print(a.search('清華大學'))
#經過測試,稍微大一點的數據也是能跑出來的.
View Code
對上面代碼加入給前綴找全部符合前綴的單詞也很容易.其實就是layer層的東西.直接提取layer這個字典選一下即可.

 自信可改變未來,問誰又能做到,簡單不過的道理,又是最難做到的道理


免責聲明!

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



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