我想搜索与列表中给定单词匹配的单词(下面的示例)。但是,有一个包含数百万字的列表。执行此搜索的最有效方法是什么?我在考虑对每个列表进行标记并将这些单词放在哈希表中。然后执行单词搜索/匹配并检索包含该单词的单词列表。从我所看到的是这个操作将采取O(n)操作。还有其他方法吗?可能没有使用哈希表?。
words_list = ['yek', 'lion', 'opt'];
# e.g. if we were to search or match the word "key" with the words in the list we should get the word "yek" or a list of words if there many that match
此外,是否有可以执行高效搜索的python库或第三方软件包?
答案 0 :(得分:1)
当你在这里指的是“匹配”时,并不完全清楚,但是如果你可以将其减少为身份比较,那么问题就会减少到设定查询,即O(1)时间。
例如,如果“匹配”表示“具有完全相同的字符集”:
words_set = {frozenset(word) for word in words_list}
然后,查找一个词:
frozenset(word) in words_set
或者,如果它意味着“具有完全相同的多字符字符”(即计算重复但忽略顺序):
words_set = {sorted(word) for word in words_list}
sorted(word) in words_set
...或者,如果您愿意:
words_set = {collections.Counter(word) for word in words_list}
collections.Counter(word) in words_set
无论哪种方式,关键(没有双关语......但也许它应该是)这里的想法是想出一个转换,将你的值(字符串)变成相同的值,如果它们匹配(一组字符,多字符字符,有序字符的有序列表等。然后,集合的整个点是它可以在恒定时间内查找等于您的值的值。
当然,转换列表需要O(N)时间(除非你只是在第一时间构建转换后的集合,而不是构建列表然后转换它),但是你可以反复使用它,它需要每次O(1)时间而不是O(N),这听起来像你在乎。
如果你需要找回匹配的单词,而不是只知道有一个单词,你仍然可以使用一个集合来执行此操作,但是使用dict更容易(如果你可以浪费一些空间): / p>
words_dict = {frozenset(word): word for word in words_list}
words_dict[frozenset(word)] # KeyError if no match
如果可能有多个匹配,只需将dict更改为multidict:
words_dict = collections.defaultdict(set)
for word in words_list:
words_dict[frozenset(word)].add(word)
words_dict[frozenset(word)] # empty set if no match
或者,如果您明确希望它是一个列表而不是一个集合:
words_dict = collections.defaultdict(list)
for word in words_list:
words_dict[frozenset(word)].append(word)
words_dict[frozenset(word)] # empty list if no match
如果你想不使用哈希表(为什么?),你可以使用搜索树或其他对数数据结构:
import blist # pip install blist to get it
words_dict = blist.sorteddict()
for word in words_list:
words_dict.setdefault(word, set()).add(word)
words_dict[frozenset(word)] # KeyError if no match
这看起来几乎完全相同,除了将defaultdict
包裹在blist.sorteddict
周围并不是一件容易的事情 - 但这只需要几行代码。 (也许你真的想要一个KeyError
而不是一个空集,所以我认为值得在某处用setdefault显示defaultdict和普通dict,所以你可以选择。)
但是在幕后,它使用混合B树变体而不是哈希表。虽然这是O(log N)时间而不是O(1),但在某些情况下它实际上比dict更快。