基于 double array 实现汉字的trie树索引 与 查询功能 python实现

一、 基本原理。

基本原理利用字符串集合中字符串的公共前缀来降低时间开销以达到提高效率的目的。

性质:1,根结点不包含任何字符信息;2,如果字符的种数为n(如英文的26个字母),则每个结点的出度为n(这样必然会导致浪费很多空间,这也是trie的缺点,我还没有想到好点的办法避免);3,查找,插入复杂度为O(n),n为字符串长度。

具体请百度之。

《基于 double array 实现汉字的trie树索引 与 查询功能 python实现》《基于 double array 实现汉字的trie树索引 与 查询功能 python实现》

二、基于 trie字典的汉字串检索代码(python):

# coding=gbk
dicts = ['啊','阿根廷','阿胶','阿拉伯','阿拉伯人','埃及']
# 字后缀数组
sufarr = {'啊':[],'阿':['根','胶','拉'],'埃':['及'],
          '根':['廷'],'胶':[],'拉':['伯'],'及':[],
          '廷':[],'伯':['人'],'人':[]}
# 按ID编码
codes = [ '啊','阿','埃','根','胶','拉','及','廷','伯','人' ]
# 字的层次标示
level = [1, 1, 1, 2, 2, 2, 2, 3, 3, 4]

# 构建双数组
base = [0]*21           # len:21 ; 21=2*10+1 的素数
check = [0]*21
# 记录已确定的字的base索引
bid_mark = {'啊':0,'阿':1,'埃':2, '根':0,'胶':0,'拉':0,'及':0,'廷':0,'伯':0,'人':0}
base[:3]=[1]*3          # 首字部分,简单置为1
def pre_mark(pre_word,wi,word):
    ''' 判断base中wi下标的前缀都在pre_words词表直到首字 且word为wi下标对应的字 '''
    bkeys = bid_mark.keys()    
    if word not in bkeys or bid_mark[word]!=wi:             # 当前字的base值不匹配
        return False
    pre_wi = check[wi]                     # 逆序遍历base的前缀
    pid = -1                               # 逆序遍历字的前缀 
    while pre_wi!=0 and (-1)*pid<=len(pre_word):
        if pre_word[pid] not in bkeys or bid_mark[pre_word[pid]]!=pre_wi:
            return False
        pre_wi = check[pre_wi]
        pid -= 1
    if (-1)*pid>len(pre_word): return True # 完成前缀的遍历
    else: return False

for w in dicts:         
    cw = [ w[i:i+2] for i in range(0,len(w),2) ]            # 分割出字
    for iid in range(1,len(cw)):
        par_w = cw[iid-1]                   # 字的前缀
        iid_w = cw[iid]                     # 当前的字
        par_id = codes.index(par_w)         # 字的前缀的编码
        wid = codes.index(iid_w)
        bval = bid_mark[par_w]
        #if iid_w=='及': print "ID: %d, word: %s, pre-wrod: %s, code: %d, pre-code: %d, pre-base:%d"%(
        #    iid,iid_w,par_w,wid,par_id,bval)
        for wi in range(base[bval]+wid,21):   # 遍历所有可能的base位置
            # 找到其base-id,置位
            p_mk = pre_mark(cw[:iid],wi,iid_w)  
            print "TEST::pre_mark() base-id: %d, word: %s, result: %s"%(wi,iid_w,str(p_mk))
            if p_mk:                              # 该词及其前缀部分已经设置值
                break
            if (not base[wi] and not check[wi]):  # 该位置未使用或吻合前缀
                check[wi] = bval 
                # 设置字的 base[] 值
                for tval in range(1,21-wi):    # 遍历所有可能的取值
                    flag = True
                    for suf_w in sufarr[iid_w]:
                        chi_id = codes.index(suf_w)     # 字的后缀的编码
                        if  base[tval+chi_id]!=0 or check[tval+chi_id]!=0:
                            flag = False
                            break              # tval不满足child的base check 为0,再尝试其他值
                    if flag:
                        base[wi] = tval        # 成功,保存该base[]
                        if wi==6 or wi==-1 or wi==9:
                            print "ID: %d, word: %s, pre-word: %s, base: %d, base-val: %d, check: %d"%(
                                iid,iid_w,par_w,wi,tval,check[wi])
                        break
                if not base[wi]:               # 无合适的 base 
                    print "ERROR:: set %s's base[]" % iid_w
                    exit(1)
                bid_mark[iid_w] = wi #; print "finnished one! %s=%d" % (iid_w,wi)
                break                          # base check均成功
        if not base[wi]: 
            print "ERROR:: get %s's position in base[]" % iid_w
            exit(2)

# 修改词尾标志
for w in bid_mark.keys():
    if len(sufarr[w])==0:
        eid = bid_mark[w]
        base[eid] *= -1

# 查询给定词    
def next_check(wid, code):
    ''' 验证两个base[]索引id间是否有连接 '''
    if code<0 or code>=len(codes):          # 字编码无效
        return False
    chiid = abs(base[wid])+code
    if check[chiid]!=wid:
        return False
    return True
qw = dicts[1] ;     # 查询词
res = ''            # 返回结果
words=[ qw[w:w+2] for w in range(0,len(qw),2) ]
head_bid = bid_mark[words[0]] ; res = ''.join(words[0])
for w in words[1:]:
    cod = codes.index(w)
    if not next_check(head_bid,cod):
        print "word: %s no find!" % qw
        break
    head_bid = abs(base[head_bid])+cod      # 更新id
    res = ''.join([res,w])
    if base[head_bid]<0:
        print "find a word: %s in Search word: %s"%(res,qw)    

           

参考: http://blog.csdn.net/joylnwang/article/details/6825991


    原文作者:Trie树
    原文地址: https://blog.csdn.net/stund/article/details/7438656
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞