如果你谷歌“cool data structures”,你搜索到的第一个结果就会是这个内容。这是一个stackoverflow问题:“有哪些数据结构比较少人知道,但是很有用?”,点赞最多的第一条答案就是字典树。我仔细研究了这些关于字典树答案,在其应用方面发现了一些有趣的事情(也发现我自己也是这种谷歌“cool data structures”的那种人),所以,我来到xcode playground,写下这些代码。
字典树就是前缀树,是一种递归数据结构:每个字典树都包含其他的子树,可以通过前缀来识别。
这是一种比较时髦的数据结构,并没有被广泛的应用,但是确有一些非常实用的应用。它有类似集合的操作,包括插入和搜索操作时间复杂度在O(n),其中n是搜索序列的长度。集合是可hash的无序元素的唯一方式,但是,针对有序的hash元素序列,字典树或许更适合你。(有一件事需要告诉你,就是集合能够针对元素自身hash,所以,如果你想存储的序列是无序的,一个元素是集合的集合是更适合的。)
A trie for keys “A”, “to”, “tea”, “ted”, “ten”, “i”, “in”, and “inn”.
在Swift里面,我们能够使每一个字典树包含一个字典,字典包含前缀和字典树。类似这样:
public struct Trie<Element : Hashable> {
private var children: [Element:Trie<Element>]
}
复制代码
我们不会遇到结构体不能递归的情况,因为我们不直接存储一个字典树在一个字典树里面——我们存储的是一个字典,并且会关联到子字典树,在这个字典里面,前缀是这个字典的键值。所以,怎么补充接下来的内容呢(初始化)?我们可以像列表生成器一样分解属性:
extension Trie {
private init<G : GeneratorType where G.Element == Element>(var gen: G) {
if let head = gen.next() {
children = [head: Trie(gen: gen)]
} else {
children = [:]
}
}
public init<S : SequenceType where S.Generator.Element == Element>(_ seq: S) {
self.init(gen: seq.generator())
}
}
复制代码
这还不够,想要存储一个序列,我们还要一个insert
函数,方法如下:
extension Trie {
private mutating func insert<G : GeneratorType where G.Element = Element>(var gen: G) {
if let head = gen.next() {
children[head]?.insert(gen) ?? {children[head] == Trie(gen: gen)}()
}
}
public mutating func insert<S : SequenceType where S.Generator.Element == Element>(_ seq: S) {
insert(seq.generate())
}
}
复制代码