diff --git a/3Sum and 4Sum/4Sum.playground/Contents.swift b/3Sum and 4Sum/4Sum.playground/Contents.swift index 37f75918c..e5b9225e0 100644 --- a/3Sum and 4Sum/4Sum.playground/Contents.swift +++ b/3Sum and 4Sum/4Sum.playground/Contents.swift @@ -3,19 +3,7 @@ print("Hello, Swift 4.2!") #endif -extension Collection where Element: Equatable { - - /// In a sorted collection, replaces the given index with a successor mapping to a unique element. - /// - /// - Parameter index: A valid index of the collection. `index` must be less than `endIndex` - func formUniqueIndex(after index: inout Index) { - var prev = index - repeat { - prev = index - formIndex(after: &index) - } while index < endIndex && self[prev] == self[index] - } -} + extension BidirectionalCollection where Element: Equatable { @@ -32,33 +20,49 @@ extension BidirectionalCollection where Element: Equatable { } func fourSum(_ collection: T, target: T.Element) -> [[T.Element]] where T.Element: Numeric & Comparable { - let sorted = collection.sorted() - var ret: [[T.Element]] = [] + let sorted = collection.sorted() + var ret: [[T.Element]] = [] - var l = sorted.startIndex - FourSum: while l < sorted.endIndex { defer { sorted.formUniqueIndex(after: &l) } - var ml = sorted.index(after: l) + var l = sorted.startIndex + FourSum: while l < sorted.endIndex { + // Skip over any repeated elements + while l < sorted.endIndex - 1, sorted[l] == sorted[sorted.index(after: l)] { + sorted.formIndex(after: &l) + } + defer { sorted.formIndex(after: &l) } + var ml = sorted.index(after: l) - ThreeSum: while ml < sorted.endIndex { defer { sorted.formUniqueIndex(after: &ml) } - var mr = sorted.index(after: ml) - var r = sorted.index(before: sorted.endIndex) + ThreeSum: while ml < sorted.endIndex { + // Skip over any repeated elements + while ml < sorted.endIndex - 1, sorted[ml] == sorted[sorted.index(after: ml)] { + sorted.formIndex(after: &ml) + } + defer { sorted.formIndex(after: &ml) } + var mr = sorted.index(after: ml) + var r = sorted.index(before: sorted.endIndex) - TwoSum: while mr < r && r < sorted.endIndex { - let sum = sorted[l] + sorted[ml] + sorted[mr] + sorted[r] - if sum == target { - ret.append([sorted[l], sorted[ml], sorted[mr], sorted[r]]) - sorted.formUniqueIndex(after: &mr) - sorted.formUniqueIndex(before: &r) - } else if sum < target { - sorted.formUniqueIndex(after: &mr) - } else { - sorted.formUniqueIndex(before: &r) + TwoSum: while mr < r && r < sorted.endIndex { + let sum = sorted[l] + sorted[ml] + sorted[mr] + sorted[r] + if sum == target { + ret.append([sorted[l], sorted[ml], sorted[mr], sorted[r]]) + // Skip over any repeated elements + while mr < r, sorted[mr] == sorted[sorted.index(after: mr)] { + sorted.formIndex(after: &mr) + } + while mr < r, sorted[r] == sorted[sorted.index(before: r)] { + sorted.formIndex(before: &r) + } + } else if sum < target { + sorted.formIndex(after: &mr) + } else { + sorted.formIndex(before: &r) + } + } } - } } - } - return ret + return ret } + // answer: [[-2, -1, 1, 2], [-2, 0, 0, 2], [-1, 0, 0, 1]] fourSum([1, 0, -1, 0, -2, 2], target: 0) diff --git a/Bloom Filter/BloomFilter.playground/Contents.swift b/Bloom Filter/BloomFilter.playground/Contents.swift index 7a3719d57..1e5b64145 100644 --- a/Bloom Filter/BloomFilter.playground/Contents.swift +++ b/Bloom Filter/BloomFilter.playground/Contents.swift @@ -1,82 +1,84 @@ //: Playground - noun: a place where people can play - -public class BloomFilter { - fileprivate var array: [Bool] - private var hashFunctions: [(T) -> Int] - - public init(size: Int = 1024, hashFunctions: [(T) -> Int]) { - self.array = [Bool](repeating: false, count: size) - self.hashFunctions = hashFunctions - } - - private func computeHashes(_ value: T) -> [Int] { - return hashFunctions.map { hashFunc in abs(hashFunc(value) % array.count) } - } - - public func insert(_ element: T) { - for hashValue in computeHashes(element) { - array[hashValue] = true +import Foundation + +public class BloomFilter { + fileprivate var array: [Bool] + private var hashFunctions: [(T) -> Int] + + public init(size: Int = 1024, hashFunctions: [(T) -> Int]) { + if (hashFunctions.count > size) { + fatalError("Number of hash functions should be less than or equal to size of array") + } + self.array = [Bool](repeating: false, count: size) + self.hashFunctions = hashFunctions } - } - public func insert(_ values: [T]) { - for value in values { - insert(value) + 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) + public func insert(_ element: T) { + for hashValue in computeHashes(element) { + array[hashValue] = true + } + } - // Map hashes to indices in the Bloom Filter - let results = hashValues.map { hashValue in array[hashValue] } + public func insert(_ values: [T]) { + for value in values { + insert(value) + } + } - // All values must be 'true' for the query to return true + public func query(_ value: T) -> Bool { + let hashValues = computeHashes(value) - // This does NOT imply that the value is in the Bloom filter, - // only that it may be. If the query returns false, however, - // you can be certain that the value was not added. + // Map hashes to indices in the Bloom Filter + let results = hashValues.map { hashValue in array[hashValue] } - let exists = results.reduce(true, { $0 && $1 }) - return exists - } + // All values must be 'true' for the query to return true + // This does NOT imply that the value is in the Bloom filter, + // only that it may be. If the query returns false, however, + // you can be certain that the value was not added. + let exists = results.reduce(true, { $0 && $1 }) + return exists + } - public func isEmpty() -> Bool { - // As soon as the reduction hits a 'true' value, the && condition will fail. - return array.reduce(true) { prev, next in prev && !next } - } + public func isEmpty() -> Bool { + // As soon as the reduction hits a 'true' value, the && condition will fail. + return array.reduce(true) { prev, next in prev && !next } + } } /* Two hash functions, adapted from http://www.cse.yorku.ca/~oz/hash.html */ func djb2(x: String) -> Int { - var hash = 5381 - for char in x { - hash = ((hash << 5) &+ hash) &+ char.hashValue - } - return Int(hash) + var hash = 5381 + for char in x { + hash = ((hash << 5) &+ hash) &+ char.hashValue + } + return Int(hash) } func sdbm(x: String) -> Int { - var hash = 0 - for char in x { - hash = char.hashValue &+ (hash << 6) &+ (hash << 16) &- hash - } - return Int(hash) + var hash = 0 + for char in x { + hash = char.hashValue &+ (hash << 6) &+ (hash << 16) &- hash + } + return Int(hash) } /* A simple test */ - let bloom = BloomFilter(size: 17, hashFunctions: [djb2, sdbm]) bloom.insert("Hello world!") print(bloom.array) -bloom.query("Hello world!") // true -bloom.query("Hello WORLD") // false +(bloom.query("Hello world!")) // true +(bloom.query("Hello WORLD")) // false bloom.insert("Bloom Filterz") -print(bloom.array) +(bloom.array) + +(bloom.query("Bloom Filterz")) // true +(bloom.query("Hello WORLD")) // false -bloom.query("Bloom Filterz") // true -bloom.query("Hello WORLD") // true