赞
踩
本文为Python算法题集之一的代码示例
Trie(发音类似 “try”)或者说 前缀树 是一种树形数据结构,用于高效地存储和检索字符串数据集中的键。这一数据结构有相当多的应用情景,例如自动补完和拼写检查。
请你实现 Trie 类:
Trie()
初始化前缀树对象。void insert(String word)
向前缀树中插入字符串 word
。boolean search(String word)
如果字符串 word
在前缀树中,返回 true
(即,在检索之前已经插入);否则,返回 false
。boolean startsWith(String prefix)
如果之前已经插入的字符串 word
的前缀之一为 prefix
,返回 true
;否则,返回 false
。示例:
输入
["Trie", "insert", "search", "search", "startsWith", "insert", "search"]
[[], ["apple"], ["apple"], ["app"], ["app"], ["app"], ["app"]]
输出
[null, null, true, false, true, null, true]
解释
Trie trie = new Trie();
trie.insert("apple");
trie.search("apple"); // 返回 True
trie.search("app"); // 返回 False
trie.startsWith("app"); // 返回 True
trie.insert("app");
trie.search("app"); // 返回 True
提示:
1 <= word.length, prefix.length <= 2000
word
和 prefix
仅由小写英文字母组成insert
、search
和 startsWith
调用次数 总计 不超过 3 * 104
次通常优化:减少循环层次
通常优化:增加分支,减少计算集
通常优化:采用内置算法来提升计算速度
分析题目特点,分析最优解
可以尝试使用默认字典defaultdict
本题都是小写字母,因此26个元素的字典就可以保存一个层级
所有单词字符都是ASCII码,Ord值都在0-127,因此128个元素的字典可以正常使用【超时测试用例,需要128一层】
可以考虑将单词结尾信息保存在字典中,用一个单词中不会出现的字符即可,比如’#’
CheckFuncPerf
(本地化函数用时和内存占用测试模块)已上传到CSDN,地址:Python算法题集_检测函数用时和内存占用的模块NLTK
**使用默认字典,定位专门的数据类,使用类属性保存单词结尾信息
页面功能测试,马马虎虎,超过33%
import CheckFuncPerf as cfp class prenode: def __init__(self): self.chars = defaultdict(int) class Trie_base: def __init__(self): self.node = prenode() self.bEnd = False def searchPrefix(self, prefix): tmpNode = self for achar in prefix: ichar = ord(achar) - ord("a") if tmpNode.node.chars[ichar] == 0: return None tmpNode = tmpNode.node.chars[ichar] return tmpNode def insert(self, word): tmpNode = self for achar in word: ichar = ord(achar) - ord("a") if tmpNode.node.chars[ichar] == 0: tmpNode.node.chars[ichar] = Trie_base() tmpNode = tmpNode.node.chars[ichar] tmpNode.bEnd = True def search(self, word): node = self.searchPrefix(word) return node is not None and node.bEnd def startsWith(self, prefix): return self.searchPrefix(prefix) is not None tmpTrie = Trie_base() result = cfp.getTimeMemoryStr(testTrie, tmpTrie, actions) print(result['msg'], '执行结果 = {}'.format(result['result'])) # 运行结果 函数 testTrie 的运行时间为 7127.62 ms;内存使用量为 373008.00 KB 执行结果 = 99
将字典数据和单词结尾信息都保存在节点类中,创建类同时初始化字典的128个元素【按题意只需26,本类已经按超时测试改写】
页面功能测试,马马虎虎,超过65%
import CheckFuncPerf as cfp class Trie_ext1: def __init__(self): self.data = [None] * 128 self.bEnd = False def searchPrefix(self, prefix): tmpnode = self for achar in prefix: ichar = ord(achar) if not tmpnode.data[ichar]: return None tmpnode = tmpnode.data[ichar] return tmpnode def insert(self, word): tmpnode = self for achar in word: ichar = ord(achar) if not tmpnode.data[ichar]: tmpnode.data[ichar] = Trie_ext1() tmpnode = tmpnode.data[ichar] tmpnode.bEnd = True def search(self, word): tmpnode = self.searchPrefix(word) return tmpnode is not None and tmpnode.bEnd def startsWith(self, prefix): return self.searchPrefix(prefix) is not None tmpTrie = Trie_ext1() result = cfp.getTimeMemoryStr(testTrie, tmpTrie, actions) print(result['msg'], '执行结果 = {}'.format(result['result'])) # 运行结果 函数 testTrie 的运行时间为 5857.32 ms;内存使用量为 793700.00 KB 执行结果 = 99
在字典中保存单词结尾信息,将字典数据保存在节点类中,创建类时不初始化字典
页面功能测试,性能卓越,超越96%
import CheckFuncPerf as cfp class Trie_ext2: def __init__(self): self.tree = {} def insert(self, word): tree = self.tree for achar in word: if achar not in tree: tree[achar] = {} tree = tree[achar] tree["#"] = "#" def search(self, word): tree = self.tree for achar in word: if achar not in tree: return False tree = tree[achar] return "#" in tree def startsWith(self, prefix): tree = self.tree for achar in prefix: if achar not in tree: return False tree = tree[achar] return True tmpTrie = Trie_ext2() result = cfp.getTimeMemoryStr(testTrie, tmpTrie, actions) print(result['msg'], '执行结果 = {}'.format(result['result'])) # 运行结果 函数 testTrie 的运行时间为 1670.38 ms;内存使用量为 146692.00 KB 执行结果 = 99
根据本地日志分析,最优算法为第3种方式【字典保存结尾信息+无额外类】Trie_ext2
本题大概有以下结论:
import random from nltk.corpus import words word_list = list(words.words()) def testTrie(aTrie, actions): for act in actions: if act[0]==1: # insert aTrie.insert(act[1]) elif act[0]==2: # search aTrie.search(act[1]) elif act[0]==3: # startsWith aTrie.startsWith(act[1]) return 99 import random actions = [] iLen = 1000000 for iIdx in range(iLen): actions.append([random.randint(1, 3), random.choice(word_list)]) tmpTrie = Trie_base() result = cfp.getTimeMemoryStr(testTrie, tmpTrie, actions) print(result['msg'], '执行结果 = {}'.format(result['result'])) tmpTrie = Trie_ext1() result = cfp.getTimeMemoryStr(testTrie, tmpTrie, actions) print(result['msg'], '执行结果 = {}'.format(result['result'])) tmpTrie = Trie_ext2() result = cfp.getTimeMemoryStr(testTrie, tmpTrie, actions) print(result['msg'], '执行结果 = {}'.format(result['result'])) # 算法本地速度实测比较 函数 testTrie 的运行时间为 7127.62 ms;内存使用量为 373008.00 KB 执行结果 = 99 函数 testTrie 的运行时间为 5857.32 ms;内存使用量为 793700.00 KB 执行结果 = 99 函数 testTrie 的运行时间为 1670.38 ms;内存使用量为 146692.00 KB 执行结果 = 99
本文代码已上传到CSDN,地址:**Python算法题源代码_LeetCode(力扣)_**实现Trie(前缀树)
一日练,一日功,一日不练十日空
may the odds be ever in your favor ~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。