集合(Sets)
布隆过滤器
布隆过滤器是一种节省空间的数据结构 (哈希表空间换时间),可以告诉我们元素是否存在于集合中。
这是一个概率数据结构:对布隆过滤器的查询返回 false,意味着该元素肯定不在集合中。如果返回 true,这意味着元素可能在集合中。
所以布隆过滤器告诉你,“绝对不是”或“可能是的”。
将对象插入集合中
布隆过滤器本质上是一个固定长度的位向量,一个位数组。当我们插入对象时,我们将其中一些位设置为1,当我们查询对象时,我们检查某些位是0还是1。两个操作都使用哈希函数。
要在过滤器中插入元素,可以使用多个不同的哈希函数对元素进行哈希。每个哈希函数返回一个我们映射到数组中索引的值。然后,我们将这个索引处的位设置为1或true。
查询集合
类似于插入,查询是通过首先对期望值进行哈希来实现的,该期望值给出几个数组索引,然后检查这些索引处的所有为是否为1。如果其中一个位不是1,则该元素没有被插入过,并且查询返回 false。如果所有为都是1,则查询返回 true。
使用布隆过滤器无法删除,因为任何一个位都有可能属于多个元素。一旦你添加了一个元素,它就在那里。
布隆过滤器的性能是O(k),其中k是哈希函数的数量。
初始化
public init(size: Int = 1024, hashFunctions: [(T) -> Int]) {
self.array = [Bool](repeating: false, count: size)
self.hashFunctions = hashFunctions
}
插入
public func insert(_ element: T) {
for hashValue in computeHashes(element) {
array[hashValue] = true
}
}
private func computeHashes(_ value: T) -> [Int] {
return hashFunctions.map() { hashFunc in abs(hashFunc(value) % array.count) }
}
查询
public func query(_ value: T) -> Bool {
let hashValues = computeHashes(value)
let results = hashValues.map() { hashValue in array[hashValue] }
let exists = results.reduce(true, { $0 && $1})
return exist
}
哈希集合
基础
public struct HashSet<T: Hashable> {
fileprivate var dictionary = Dictionary<T, Bool>()
public init() {
}
public mutating func insert(_ element: T) {
dictionary[element] = true
}
public mutating func remove(_ element: T) {
dictionary[element] = nil
}
public func contains(_ element: T) -> Bool {
return dictionary[element] != nil
}
public func allElements() -> [T] {
return Array(dictionary.keys)
}
public var count: Int {
return dictionary.count
}
public var isEmpty: Bool {
return dictionary.isEmpty
}
}
合并
extension HashSet {
public func union(_ otherSet: HashSet<T>) -> HashSet<T> {
var combined = HashSet<T>()
for obj in self.dictionary.keys {
combined.insert(obj)
}
for obj in otherSet.dictionary.keys {
combined.insert(obj)
}
return combined
}
}
并集
extension HashSet {
public func intersect(_ otherSet: HashSet<T>) -> HashSet<T> {
var common = HashSet<T>()
for obj in dictionary.keys {
if otherSet.contains(obj) {
common.insert(obj)
}
}
return common
}
}
A - B
extension HashSet {
public func difference(_ otherSet: HashSet<T>) -> HashSet<T> {
var diff = HashSet<T>()
for obj in dictionary.keys {
if !otherSet.contains(obj) {
diff.insert(obj)
}
}
return diff
}
}
多重集
var set = Multiset<Int>()
set.add(1) // set is now [1]
set.add(2) // set is now [1, 2]
set.add(2) // set is now [1, 2, 2]
这看起来像是一个数组。差异是:
- 顺序:数组维护添加到它的项的顺序,多重集合没有
- 测试成员资格:测试元素是否是其成员,数组是
O(n),多重集合是O(1) - 测试子集:测试集合X是否是集合Y的子集,对于多重集而言是一个简单的操作,但对于数组来说是复杂的。
使用字典实现
public struct Multiset<Element: Hashable> {
private var storage: [Element: UInt] = [:]
public init() {}
}
添加元素
public mutating func add (_ elem: Element) {
storage[elem, default: 0] += 1
}
删除元素
public mutating func remove(_ elem: Element) {
if let currentCount = storage[elem] {
if currentCount > 1 {
storage[elem] = currentCount - 1
} else {
storage.removeValue(forKey: elem)
}
}
}
有序集
首先看看APPLE实现的有序集是怎样表现的:
let s = AppleOrderedSet<Int>()
s.add(1)
s.add(2)
s.add(-1)
s.add(0)
s.insert(4, at:3)
print(s.all()) // [1, 2, -1, 4, 0]
s.set(-1, at: 0) // 已经有-1在index: 2,因此这个操作不做任何事情
print(s.all()) // [1, 2, -1, 4, 0]
s.remove(-1)
print(s.all()) // [1, 2, 4, 0]
print(s.object(at: 1)) // 2
print(s.object(at: 2)) // 4
很容易考虑哈希表实现
var indexOfKey: [T: Int]
var objects: [T]
添加
// O(1)
public func add(_ object: T) {
guard indexOfKey[object] == nil else {
return
}
objects.append(object)
indexOfKey[object] = objects.count - 1
}
插入
// O(n)
public func insert(_ object: T, at index: Int) {
assert(index < objects.count, "Index should be smaller than object count")
assert(index >= 0, "Index should be bigger than 0")
guard indexOfKey[object] == nil else {
return
}
objects.insert(object, at: index)
indexOfKey[object] = index
for i in index+1..<objects.count {
indexOfKey[objects[i]] = i
}
}
设置(如果object已存在于OrderedSet中,则什么也不做。否则,我们需要更新indexOfKey和objects。)
// O(1)
public func set(_ object: T, at index: Int) {
assert(index < object.count, "Index should be smaller than object count")
assert(index >= 0, "Index should be bigger than 0")
guard indexOfKey[object] == nil else {
return
}
indexOfKey.removeValue(forKey: objects[index])
indexOfKey[object] = index
objects[index] = object
}
删除
// O(n)
public func remove(_ object: T) {
guard let index = indexOfKey[object] else {
return
}
indexOfKey.removeValue(forKey: object)
objects.remove(at: index)
for i in index..<objects.count {
indexOfKey[objects[i]] = i
}
}