复杂数据结构-前缀树(trie tree)

节点(pass,end,nexts)
pass:有多少个节点划过
end:有多少个字符串以该节点结尾
nexts数组:有多少条边,设为26长度的数组(表示从a-z),值为空表示不存在这条路,为node表示存在(即这条路的下一个节点)。
也可用hashmap表示:(key:a的ascii码,value:对应的node)
应用:
1、查询一个字符串(eg:hello)在数据集中出现过多少次:(哈希表可以也可以实现)
从h开始寻找,求hello尾节点的end值
2、查找多少词以某个单词(eg:hel)开头,求“l”的pass值。(哈希表无法实现)【复杂度于查询单词长度有关,与样本量无关】

注1:通过修改节点信息,可以实现很多功能
注2:改代码只能处理a-z的字符串,可用哈希表代替nexts数组实现更复杂功能。(key:a的ascii码,value:对应的node)

代码:

class TrieNode():
    def __init__(self):
        self.passs=0
        self.end=0
        self.nexts=[None for i in range(26)] #限制字符串中的字母“a-z”
class Trie():
    def __init__(self):
        self.root=None
    def makeTrie(self):
        self.root=TrieNode()
    def insert(self,word):             #插入一个字符串的方法
        if word==None:
            return
        chs=list(word)  #将字符串转化为一个个字母
        node=self.root
        index=0
        for i in range(len(chs)):
            index=ord(chs[i])-ord('a')#将字母转化成数字下标a-0,b-1,c-2....z-25
            if node.nexts[index]==None: #如果没有,建立这个边
                node.nexts[index]=TrieNode()
            node=node.nexts[index] #node往下级跑
            node.passs+=1 #沿途节点的pass值加一
        node.end+=1 #尾部节点的end值加一

    def search(self,word):              #查字符串出现了几次
        if word==None:
            return 0
        chs = list(word)  # 将字符串转化为一个个字母
        node = self.root
        index = 0
        for i in range(len(chs)):
            index=ord(chs[i])-ord('a') #将字母转化成数字下标a-0,b-1,c-2....z-25
            if node.nexts[index]==None: #如果走不下去,返回0
                return 0
            node=node.nexts[index]      #继续往下走
        return node.end                #返回最后一个节点的end值

    def dellete(self, word):           #删除一个字符串的方法
        if self.search(word) != 0:  #判断是否存在
            chs = list(word)  # 将字符串转化为一个个字母
            node = self.root
            #node.passs-=1
            index = 0
            for i in range(len(chs)):
                index = ord(chs[i]) - ord('a')  # 将字母转化成数字下标a-0,b-1,c-2....z-25
                node.nexts[index].passs-=1      #下一节点的pass值在减一之后是否等于0
                if node.nexts[index].passs ==0 :  # 如果等于0,整条链直接全舍去
                    node.nexts[index] = None
                    return
                node = node.nexts[index]  # node往下级跑
            node.end-= 1  # 沿途节点的end值减一

    def prefixnumber(self,word):       #查找以word开头的字符串个数
        if word==None:
            return 0
        chs = list(word)  # 将字符串转化为一个个字母
        node = self.root
        index = 0
        for i in range(len(chs)):
            index = ord(chs[i]) - ord('a')  # 将字母转化成数字下标a-0,b-1,c-2....z-25
            if node.nexts[index]== None:  # 进行不下去,说明没有字符串以该word开头
                return 0
            node = node.nexts[index]
        return node.passs
trie=Trie()
trie.makeTrie()
print(trie.search("chen"))
trie.insert("chen")
print(trie.search("chen"))
trie.dellete("chen")
print(trie.search("chen"))
trie.insert("chen")
trie.insert("chen")
trie.insert("fei")
trie.insert("ting")
trie.insert("chena")
trie.insert("chenb")
print(trie.search("chen"))
print(trie.search("fei"))
print(trie.search("ting"))
print(trie.prefixnumber("chen"))

应用1:
子数组的最大异或和

方法1:预处理数组

def getmax1(arr):
    eor=0
    dp=[]
    maxx=float("-inf")
    for i in range(len(arr)):
        eor^=arr[i]  #0...i
        maxx=max(maxx,eor)
        for j in range(1,i+1):
            cureor=eor^dp[j-1] #j..i=0...i^0...j-1
            maxx=max(cureor,maxx)
        dp[i]=eor
    return maxx

方法2:前缀树

#0..i和0...?异或和最大
#首位相同,其他位相反
def getmax2(arr):
    if not  arr or len(arr)==0:
        return 0
    eor=0
    maxx=float("-inf")
    numtrie=NumTrie()
    for i in range(len(arr)):
        eor^=arr[i] #0...i
        maxx=max(maxx,numtrie.maxeor(eor))
        numtrie.addd(eor)
    return maxx

class Node:
    def __init__(self):
        self.nexts=[None]*2      #0,1
class NumTrie:
    def __init__(self):
        self.head=Node()
    def addd(self,num):
        cur=self.head
        for i in range(31,-1,-1):
            path=(num>>i)&1   #提取出每一个位置上的数字
            if cur.nexts[path]==None:
                cur.nexts[path]=Node()
            cur=cur.nexts[path]
    #0...i
    def maxeor(self,num):
        cur=self.head
        res=0
        for i in range(31,-1,-1):
            path=(num>>i)&1   #提取出每一个位置上的数字
            if i==31:         #符号位,相同,期待的位置
                best=path
            else:             #数字位,取反
                best=path^1
            if cur.nexts[best]==None:
                best^=1       #best路为空,被迫走另一条路
            res|=(path^best)<<i
            cur=cur.nexts[best]  #继续往下走
        return res

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
应用2:
题目:

给定一个非空数组,数组中元素为 a0, a1, a2, … , an-1,其中 0 ≤ ai < 231 。
找到 ai 和aj 最大的异或 (XOR) 运算结果,其中0 ≤ i, j < n 。
你能在O(n)的时间解决这个问题吗?
示例:
输入: [3, 10, 5, 25, 2, 8]

输出: 28

解释: 最大的结果是 5 ^ 25 = 28.

class Node:
    def __init__(self):
        self.nexts=[None]*2      #0,1
class NumTrie:
    def __init__(self):
        self.head=Node()
    def addd(self,num):
        cur=self.head
        for i in range(31,-1,-1):
            path=(num>>i)&1   #提取出每一个位置上的数字
            if cur.nexts[path]==None:
                cur.nexts[path]=Node()
            cur=cur.nexts[path]
    def maxeor(self,num):
        cur=self.head
        res=0
        for i in range(31,-1,-1):
            path=(num>>i)&1    #提取出每一个位置上的数字
            if i==31:          #符号位,相同,期待的位置
                best=path
            else:              #数字位,取反
                best=path^1
            if cur.nexts[best]==None:
                best^=1        #best路为空,被迫走另一条路
            res|=(path^best)<<i
            cur=cur.nexts[best]  #继续往下走
        return res
class Solution:
    def findMaximumXOR(self, nums) -> int:
        tire=NumTrie()
        maxx=float("-inf")
        for i in range(len(nums)):
            tire.addd(nums[i])
        for i in range(len(nums)):
            maxx=max(maxx,tire.maxeor(nums[i]))
        return maxx

猜你喜欢

转载自blog.csdn.net/weixin_40876685/article/details/88947252