From 95b45f038bfaba63fac2621c24dd3bd0bd9d59be Mon Sep 17 00:00:00 2001 From: remlostime Date: Sat, 18 Nov 2017 20:08:36 -0800 Subject: [PATCH] Split sorted set and ordered set --- .../Sources/AppleOrderedSet.swift | 76 ---- .../contents.xcplayground | 4 - Ordered Set/AppleOrderedSet.swift | 76 ---- .../Contents.swift | 2 +- .../timeline.xctimeline | 6 - .../Sources/OrderedSet.swift | 171 ++++----- .../contents.xcplayground | 8 +- Ordered Set/OrderedSet.swift | 172 ++++----- Ordered Set/README.markdown | 336 +----------------- Sorted Set/README.markdown | 334 +++++++++++++++++ .../Example 1.xcplaygroundpage/Contents.swift | 2 +- .../timeline.xctimeline | 6 +- .../Example 2.xcplaygroundpage/Contents.swift | 2 +- .../Example 3.xcplaygroundpage/Contents.swift | 2 +- .../Sources/Player.swift | 6 +- .../Sources/Random.swift | 4 +- .../Sources/SortedSet.swift | 118 ++++++ .../contents.xcplayground | 8 + .../contents.xcworkspacedata | 0 Sorted Set/SortedSet.swift | 118 ++++++ .../contents.xcworkspacedata | 19 +- 21 files changed, 738 insertions(+), 732 deletions(-) delete mode 100644 Ordered Set/AppleOrderedSet.playground/Sources/AppleOrderedSet.swift delete mode 100644 Ordered Set/AppleOrderedSet.playground/contents.xcplayground delete mode 100644 Ordered Set/AppleOrderedSet.swift rename Ordered Set/{AppleOrderedSet.playground => OrderedSet.playground}/Contents.swift (91%) delete mode 100644 Ordered Set/OrderedSet.playground/Pages/Example 3.xcplaygroundpage/timeline.xctimeline create mode 100644 Sorted Set/README.markdown rename {Ordered Set/OrderedSet.playground => Sorted Set/SortedSet.playground}/Pages/Example 1.xcplaygroundpage/Contents.swift (94%) rename {Ordered Set/OrderedSet.playground => Sorted Set/SortedSet.playground}/Pages/Example 1.xcplaygroundpage/timeline.xctimeline (50%) rename {Ordered Set/OrderedSet.playground => Sorted Set/SortedSet.playground}/Pages/Example 2.xcplaygroundpage/Contents.swift (95%) rename {Ordered Set/OrderedSet.playground => Sorted Set/SortedSet.playground}/Pages/Example 3.xcplaygroundpage/Contents.swift (95%) rename {Ordered Set/OrderedSet.playground => Sorted Set/SortedSet.playground}/Sources/Player.swift (95%) rename {Ordered Set/OrderedSet.playground => Sorted Set/SortedSet.playground}/Sources/Random.swift (88%) create mode 100644 Sorted Set/SortedSet.playground/Sources/SortedSet.swift create mode 100644 Sorted Set/SortedSet.playground/contents.xcplayground rename {Ordered Set/OrderedSet.playground => Sorted Set/SortedSet.playground}/playground.xcworkspace/contents.xcworkspacedata (100%) create mode 100644 Sorted Set/SortedSet.swift diff --git a/Ordered Set/AppleOrderedSet.playground/Sources/AppleOrderedSet.swift b/Ordered Set/AppleOrderedSet.playground/Sources/AppleOrderedSet.swift deleted file mode 100644 index 6bd8f94a0..000000000 --- a/Ordered Set/AppleOrderedSet.playground/Sources/AppleOrderedSet.swift +++ /dev/null @@ -1,76 +0,0 @@ -public class AppleOrderedSet { - private var objects: [T] = [] - private var indexOfKey: [T: Int] = [:] - - public init() {} - - // 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.. T { - assert(index < objects.count, "Index should be smaller than object count") - assert(index >= 0, "Index should be bigger than 0") - - return objects[index] - } - - // O(1) - public func set(_ 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 - } - - indexOfKey.removeValue(forKey: objects[index]) - indexOfKey[object] = index - objects[index] = object - } - - // O(1) - public func indexOf(_ object: T) -> Int { - return indexOfKey[object] ?? -1 - } - - // 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.. [T] { - return objects - } -} diff --git a/Ordered Set/AppleOrderedSet.playground/contents.xcplayground b/Ordered Set/AppleOrderedSet.playground/contents.xcplayground deleted file mode 100644 index 5da2641c9..000000000 --- a/Ordered Set/AppleOrderedSet.playground/contents.xcplayground +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/Ordered Set/AppleOrderedSet.swift b/Ordered Set/AppleOrderedSet.swift deleted file mode 100644 index 6bd8f94a0..000000000 --- a/Ordered Set/AppleOrderedSet.swift +++ /dev/null @@ -1,76 +0,0 @@ -public class AppleOrderedSet { - private var objects: [T] = [] - private var indexOfKey: [T: Int] = [:] - - public init() {} - - // 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.. T { - assert(index < objects.count, "Index should be smaller than object count") - assert(index >= 0, "Index should be bigger than 0") - - return objects[index] - } - - // O(1) - public func set(_ 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 - } - - indexOfKey.removeValue(forKey: objects[index]) - indexOfKey[object] = index - objects[index] = object - } - - // O(1) - public func indexOf(_ object: T) -> Int { - return indexOfKey[object] ?? -1 - } - - // 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.. [T] { - return objects - } -} diff --git a/Ordered Set/AppleOrderedSet.playground/Contents.swift b/Ordered Set/OrderedSet.playground/Contents.swift similarity index 91% rename from Ordered Set/AppleOrderedSet.playground/Contents.swift rename to Ordered Set/OrderedSet.playground/Contents.swift index c5548c2c2..6a2415231 100644 --- a/Ordered Set/AppleOrderedSet.playground/Contents.swift +++ b/Ordered Set/OrderedSet.playground/Contents.swift @@ -1,4 +1,4 @@ -let s = AppleOrderedSet() +let s = OrderedSet() s.add(1) s.add(2) diff --git a/Ordered Set/OrderedSet.playground/Pages/Example 3.xcplaygroundpage/timeline.xctimeline b/Ordered Set/OrderedSet.playground/Pages/Example 3.xcplaygroundpage/timeline.xctimeline deleted file mode 100644 index bf468afec..000000000 --- a/Ordered Set/OrderedSet.playground/Pages/Example 3.xcplaygroundpage/timeline.xctimeline +++ /dev/null @@ -1,6 +0,0 @@ - - - - - diff --git a/Ordered Set/OrderedSet.playground/Sources/OrderedSet.swift b/Ordered Set/OrderedSet.playground/Sources/OrderedSet.swift index 8490a3320..5508c3637 100644 --- a/Ordered Set/OrderedSet.playground/Sources/OrderedSet.swift +++ b/Ordered Set/OrderedSet.playground/Sources/OrderedSet.swift @@ -1,117 +1,76 @@ -/* - An Ordered Set is a collection where all items in the set follow an ordering, - usually ordered from 'least' to 'most'. The way you value and compare items - can be user-defined. -*/ -public struct OrderedSet { - private var internalSet = [T]() - - public init() { } - - // Returns the number of elements in the OrderedSet. - public var count: Int { - return internalSet.count - } - - // Inserts an item. Performance: O(n) - public mutating func insert(_ item: T) { - if exists(item) { - return // don't add an item if it already exists - } - - // Insert new the item just before the one that is larger. - for i in 0.. item { - internalSet.insert(item, at: i) - return - } +public class OrderedSet { + private var objects: [T] = [] + private var indexOfKey: [T: Int] = [:] + + public init() {} + + // O(1) + public func add(_ object: T) { + guard indexOfKey[object] == nil else { + return } - - // Append to the back if the new item is greater than any other in the set. - internalSet.append(item) + + objects.append(object) + indexOfKey[object] = objects.count - 1 } - - // Removes an item if it exists. Performance: O(n) - public mutating func remove(_ item: T) { - if let index = index(of: item) { - internalSet.remove(at: index) + + // 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 } - } - - // Returns true if and only if the item exists somewhere in the set. - public func exists(_ item: T) -> Bool { - return index(of: item) != nil - } - - // Returns the index of an item if it exists, or -1 otherwise. - public func index(of item: T) -> Int? { - var leftBound = 0 - var rightBound = count - 1 - - while leftBound <= rightBound { - let mid = leftBound + ((rightBound - leftBound) / 2) - - if internalSet[mid] > item { - rightBound = mid - 1 - } else if internalSet[mid] < item { - leftBound = mid + 1 - } else if internalSet[mid] == item { - return mid - } else { - // When we get here, we've landed on an item whose value is equal to the - // value of the item we're looking for, but the items themselves are not - // equal. We need to check the items with the same value to the right - // and to the left in order to find an exact match. - - // Check to the right. - for j in stride(from: mid, to: count - 1, by: 1) { - if internalSet[j + 1] == item { - return j + 1 - } else if internalSet[j] < internalSet[j + 1] { - break - } - } - - // Check to the left. - for j in stride(from: mid, to: 0, by: -1) { - if internalSet[j - 1] == item { - return j - 1 - } else if internalSet[j] > internalSet[j - 1] { - break - } - } - return nil - } + + objects.insert(object, at: index) + indexOfKey[object] = index + for i in index+1.. T { - assert(index >= 0 && index < count) - return internalSet[index] + + // O(1) + public func object(at index: Int) -> T { + assert(index < objects.count, "Index should be smaller than object count") + assert(index >= 0, "Index should be bigger than 0") + + return objects[index] } - - // Returns the 'maximum' or 'largest' value in the set. - public func max() -> T? { - return count == 0 ? nil : internalSet[count - 1] + + // O(1) + public func set(_ 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 + } + + indexOfKey.removeValue(forKey: objects[index]) + indexOfKey[object] = index + objects[index] = object } - - // Returns the 'minimum' or 'smallest' value in the set. - public func min() -> T? { - return count == 0 ? nil : internalSet[0] + + // O(1) + public func indexOf(_ object: T) -> Int { + return indexOfKey[object] ?? -1 } - - // Returns the k-th largest element in the set, if k is in the range - // [1, count]. Returns nil otherwise. - public func kLargest(_ k: Int) -> T? { - return k > count || k <= 0 ? nil : internalSet[count - k] + + // 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.. T? { - return k > count || k <= 0 ? nil : internalSet[k - 1] + + public func all() -> [T] { + return objects } } diff --git a/Ordered Set/OrderedSet.playground/contents.xcplayground b/Ordered Set/OrderedSet.playground/contents.xcplayground index 18c02c912..5da2641c9 100644 --- a/Ordered Set/OrderedSet.playground/contents.xcplayground +++ b/Ordered Set/OrderedSet.playground/contents.xcplayground @@ -1,8 +1,4 @@ - - - - - - + + \ No newline at end of file diff --git a/Ordered Set/OrderedSet.swift b/Ordered Set/OrderedSet.swift index a8b3b0966..8b6df7d3e 100644 --- a/Ordered Set/OrderedSet.swift +++ b/Ordered Set/OrderedSet.swift @@ -1,117 +1,77 @@ -/* - An Ordered Set is a collection where all items in the set follow an ordering, - usually ordered from 'least' to 'most'. The way you value and compare items - can be user-defined. -*/ -public struct OrderedSet { - private var internalSet = [T]() - - public init() { } - - // Returns the number of elements in the OrderedSet. - public var count: Int { - return internalSet.count - } - - // Inserts an item. Performance: O(n) - public mutating func insert(item: T) { - if exists(item) { - return // don't add an item if it already exists +public class OrderedSet { + private var objects: [T] = [] + private var indexOfKey: [T: Int] = [:] + + public init() {} + + // O(1) + public func add(_ object: T) { + guard indexOfKey[object] == nil else { + return } - - // Insert new the item just before the one that is larger. - for i in 0.. item { - internalSet.insert(item, atIndex: i) - return - } - } - - // Append to the back if the new item is greater than any other in the set. - internalSet.append(item) + + objects.append(object) + indexOfKey[object] = objects.count - 1 } - - // Removes an item if it exists. Performance: O(n) - public mutating func remove(item: T) { - if let index = indexOf(item) { - internalSet.removeAtIndex(index) + + // 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 } - } - - // Returns true if and only if the item exists somewhere in the set. - public func exists(item: T) -> Bool { - return indexOf(item) != nil - } - - // Returns the index of an item if it exists, or -1 otherwise. - public func indexOf(item: T) -> Int? { - var leftBound = 0 - var rightBound = count - 1 - - while leftBound <= rightBound { - let mid = leftBound + ((rightBound - leftBound) / 2) - - if internalSet[mid] > item { - rightBound = mid - 1 - } else if internalSet[mid] < item { - leftBound = mid + 1 - } else if internalSet[mid] == item { - return mid - } else { - // When we get here, we've landed on an item whose value is equal to the - // value of the item we're looking for, but the items themselves are not - // equal. We need to check the items with the same value to the right - // and to the left in order to find an exact match. - - // Check to the right. - for j in mid.stride(to: count - 1, by: 1) { - if internalSet[j + 1] == item { - return j + 1 - } else if internalSet[j] < internalSet[j + 1] { - break - } - } - - // Check to the left. - for j in mid.stride(to: 0, by: -1) { - if internalSet[j - 1] == item { - return j - 1 - } else if internalSet[j] > internalSet[j - 1] { - break - } - } - return nil - } + + objects.insert(object, at: index) + indexOfKey[object] = index + for i in index+1.. T { - assert(index >= 0 && index < count) - return internalSet[index] + + // O(1) + public func object(at index: Int) -> T { + assert(index < objects.count, "Index should be smaller than object count") + assert(index >= 0, "Index should be bigger than 0") + + return objects[index] } - - // Returns the 'maximum' or 'largest' value in the set. - public func max() -> T? { - return count == 0 ? nil : internalSet[count - 1] + + // O(1) + public func set(_ 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 + } + + indexOfKey.removeValue(forKey: objects[index]) + indexOfKey[object] = index + objects[index] = object } - - // Returns the 'minimum' or 'smallest' value in the set. - public func min() -> T? { - return count == 0 ? nil : internalSet[0] + + // O(1) + public func indexOf(_ object: T) -> Int { + return indexOfKey[object] ?? -1 } - - // Returns the k-th largest element in the set, if k is in the range - // [1, count]. Returns nil otherwise. - public func kLargest(k: Int) -> T? { - return k > count || k <= 0 ? nil : internalSet[count - k] + + // 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.. T? { - return k > count || k <= 0 ? nil : internalSet[k - 1] + + public func all() -> [T] { + return objects } } + diff --git a/Ordered Set/README.markdown b/Ordered Set/README.markdown index 11096d5bb..82d736731 100644 --- a/Ordered Set/README.markdown +++ b/Ordered Set/README.markdown @@ -1,338 +1,6 @@ # Ordered Set -## Sorted Array Version - -An Ordered Set is a collection of unique items in sorted order. Items are usually sorted from least to greatest. - -The Ordered Set data type is a hybrid of: - -- a [Set](https://en.wikipedia.org/wiki/Set_%28mathematics%29), a collection of unique items where the order does not matter, and -- a [Sequence](https://en.wikipedia.org/wiki/Sequence), an ordered list of items where each item may appear more than once. - -It's important to keep in mind that two items can have the same *value* but still may not be equal. For example, we could define "a" and "z" to have the same value (their lengths), but clearly "a" != "z". - -## Why use an ordered set? - -Ordered Sets should be considered when you need to keep your collection sorted at all times, and you do lookups on the collection much more frequently than inserting or deleting items. Many of the lookup operations for an Ordered Set are **O(1)**. - -A good example would be keeping track of the rankings of players in a scoreboard (see example 2 below). - -#### These are ordered sets - -A set of integers: - - [1, 2, 3, 6, 8, 10, 1000] - -A set of strings: - - ["a", "is", "set", "this"] - -The "value" of these strings could be their text content, but also for example their length. - -#### These are not ordered sets - -This set violates the property of uniqueness: - - [1, 1, 2, 3, 5, 8] - -This set violates the sorted property: - - [1, 11, 2, 3] - -## The code - -We'll start by creating our internal representation for the Ordered Set. Since the idea of a set is similar to that of an array, we will use an array to represent our set. Furthermore, since we'll need to keep the set sorted, we need to be able to compare the individual elements. Thus, any type must conform to the [Comparable Protocol](https://developer.apple.com/library/watchos/documentation/Swift/Reference/Swift_Comparable_Protocol/index.html). - -```swift -public struct OrderedSet { - private var internalSet = [T]() - - // Returns the number of elements in the OrderedSet. - public var count: Int { - return internalSet.count - } - ... -``` - -Lets take a look at the `insert()` function first. This first checks if the item already exists in the collection. If so, it returns and does not insert the item. Otherwise, it will insert the item through straightforward iteration. - -```swift - public mutating func insert(_ item: T){ - if exists(item) { - return // don't add an item if it already exists - } - - // Insert new the item just before the one that is larger. - for i in 0.. item { - internalSet.insert(item, at: i) - return - } - } - - // Append to the back if the new item is greater than any other in the set. - internalSet.append(item) - } -``` - -As we'll see later on, checking if the item is already in the set has an efficiency of **O(log(n) + k)** where **k** is the number of items with the same value as the item we are inserting. - -To insert the new item, the `for` loop starts from the beginning of the array, and checks to see if each item is larger than the item we want to insert. Once we find such an item, we insert the new one into its place. This shifts the rest of the array over to the right by 1 position. This loop is at worst **O(n)**. - -The total performance of the `insert()` function is therefore **O(n)**. - -Next up is the `remove()` function: - -```swift - public mutating func remove(_ item: T) { - if let index = index(of: item) { - internalSet.remove(at: index) - } - } -``` - -First this checks if the item exists and then removes it from the array. Because of the `removeAtIndex()` function, the efficiency for remove is **O(n)**. - -The next function is `indexOf()`, which takes in an object of type `T` and returns the index of the corresponding item if it is in the set, or `nil` if it is not. Since our set is sorted, we can use a binary search to quickly search for the item. - -```swift - public func index(of item: T) -> Int? { - var leftBound = 0 - var rightBound = count - 1 - - while leftBound <= rightBound { - let mid = leftBound + ((rightBound - leftBound) / 2) - - if internalSet[mid] > item { - rightBound = mid - 1 - } else if internalSet[mid] < item { - leftBound = mid + 1 - } else if internalSet[mid] == item { - return mid - } else { - // see below - } - } - return nil - } -``` - -> **Note:** If you are not familiar with the concept of binary search, we have an [article that explains all about it](../Binary%20Search). - -However, there is an important issue to deal with here. Recall that two objects can be unequal yet still have the same "value" for the purposes of comparing them. Since a set can contain multiple items with the same value, it is important to check that the binary search has landed on the correct item. - -For example, consider this ordered set of `Player` objects. Each `Player` has a name and a number of points: - - [ ("Bill", 50), ("Ada", 50), ("Jony", 50), ("Steve", 200), ("Jean-Louis", 500), ("Woz", 1000) ] - -We want the set to be ordered by points, from low to high. Multiple players can have the same number of points. The name of the player is not important for this ordering. However, the name *is* important for retrieving the correct item. - -Let's say we do `indexOf(bill)` where `bill` is player object `("Bill", 50)`. If we did a traditional binary search we'd land on index 2, which is the object `("Jony", 50)`. The value 50 matches, but it's not the object we're looking for! - -Therefore, we also need to check the items with the same value to the right and left of the midpoint. The code to check the left and right side looks like this: - -```swift - // Check to the right. - for j in mid.stride(to: count - 1, by: 1) { - if internalSet[j + 1] == item { - return j + 1 - } else if internalSet[j] < internalSet[j + 1] { - break - } - } - - // Check to the left. - for j in mid.stride(to: 0, by: -1) { - if internalSet[j - 1] == item { - return j - 1 - } else if internalSet[j] > internalSet[j - 1] { - break - } - } - - return nil -``` - -These loops start at the current `mid` value and then look at the neighboring values until we've found the correct object. - -The combined runtime for `indexOf()` is **O(log(n) + k)** where **n** is the length of the set, and **k** is the number of items with the same *value* as the one that is being searched for. - -Since the set is sorted, the following operations are all **O(1)**: - -```swift - // Returns the 'maximum' or 'largest' value in the set. - public func max() -> T? { - return count == 0 ? nil : internalSet[count - 1] - } - - // Returns the 'minimum' or 'smallest' value in the set. - public func min() -> T? { - return count == 0 ? nil : internalSet[0] - } - - // Returns the k-th largest element in the set, if k is in the range - // [1, count]. Returns nil otherwise. - public func kLargest(_ k: Int) -> T? { - return k > count || k <= 0 ? nil : internalSet[count - k] - } - - // Returns the k-th smallest element in the set, if k is in the range - // [1, count]. Returns nil otherwise. - public func kSmallest(_ k: Int) -> T? { - return k > count || k <= 0 ? nil : internalSet[k - 1] - } -``` - -## Examples - -Below are a few examples that can be found in the playground file. - -### Example 1 - -Here we create a set with random Integers. Printing the largest/smallest 5 numbers in the set is fairly easy. - -```swift -// Example 1 with type Int -var mySet = OrderedSet() - -// Insert random numbers into the set -for _ in 0..<50 { - mySet.insert(randomNum(50, max: 500)) -} - -print(mySet) - -print(mySet.max()) -print(mySet.min()) - -// Print the 5 largest values -for k in 1...5 { - print(mySet.kLargest(k)) -} - -// Print the 5 lowest values -for k in 1...5 { - print(mySet.kSmallest(k)) -} -``` - -### Example 2 - -In this example we take a look at something a bit more interesting. We define a `Player` struct as follows: - -```swift -public struct Player: Comparable { - public var name: String - public var points: Int -} -``` - -The `Player` also gets its own `==` and `<` operators. The `<` operator is used to determine the sort order of the set, while `==` determines whether two objects are really equal. - -Note that `==` compares both the name and the points: - -```swifr -func ==(x: Player, y: Player) -> Bool { - return x.name == y.name && x.points == y.points -} -``` - -But `<` only compares the points: - -```swift -func <(x: Player, y: Player) -> Bool { - return x.points < y.points -} -``` - -Therefore, two `Player`s can each have the same value (the number of points), but are not guaranteed to be equal (they can have different names). - -We create a new set and insert 20 random players. The `Player()` constructor gives each player a random name and score: - -```swift -var playerSet = OrderedSet() - -// Populate the set with random players. -for _ in 0..<20 { - playerSet.insert(Player()) -} -``` - -Insert another player: - -```swift -var anotherPlayer = Player() -playerSet.insert(anotherPlayer) -``` - -Now we use the `indexOf()` function to find out what rank `anotherPlayer` is. - -```swift -let level = playerSet.count - playerSet.indexOf(anotherPlayer)! -print("\(anotherPlayer.name) is ranked at level \(level) with \(anotherPlayer.points) points") -``` - -### Example 3 - -The final example demonstrates the need to look for the right item even after the binary search has completed. - -We insert 9 players into the set: - -```swift -var repeatedSet = OrderedSet() - -repeatedSet.insert(Player(name: "Player 1", points: 100)) -repeatedSet.insert(Player(name: "Player 2", points: 100)) -repeatedSet.insert(Player(name: "Player 3", points: 100)) -repeatedSet.insert(Player(name: "Player 4", points: 100)) -repeatedSet.insert(Player(name: "Player 5", points: 100)) -repeatedSet.insert(Player(name: "Player 6", points: 50)) -repeatedSet.insert(Player(name: "Player 7", points: 200)) -repeatedSet.insert(Player(name: "Player 8", points: 250)) -repeatedSet.insert(Player(name: "Player 9", points: 25)) -``` - -Notice how several of these players have the same value of 100 points. - -The set looks something like this: - - [Player 9, Player 6, Player 1, Player 2, Player 3, Player 4, Player 5, Player 7, Player 8] - -The next line looks for `Player 2`: - -```swift -print(repeatedSet.index(of: Player(name: "Player 2", points: 100))) -``` - -After the binary search finishes, the value of `mid` is at index 5: - - [Player 9, Player 6, Player 1, Player 2, Player 3, Player 4, Player 5, Player 7, Player 8] - mid - -However, this is not `Player 2`. Both `Player 4` and `Player 2` have the same points, but a different name. The binary search only looked at the points, not the name. - -But we do know that `Player 2` must be either to the immediate left or the right of `Player 4`, so we check both sides of `mid`. We only need to look at the objects with the same value as `Player 4`. The others are replaced by `X`: - - [X, X, Player 1, Player 2, Player 3, Player 4, Player 5, X, X] - mid - -The code then first checks on the right of `mid` (where the `*` is): - - [X, X, Player 1, Player 2, Player 3, Player 4, Player 5, X, X] - mid * - -The right side did not contain the item, so we look at the left side: - - [X, X, Player 1, Player 2, Player 3, Player 4, Player 5, X, X] - * mid - - [X, X, Player 1, Player 2, Player 3, Player 4, Player 5, X, X] - * mid - -Finally, we've found `Player 2`, and return index 3. - -## Apple's Ordered Set -Below the ordered set is based on sorted array. But [Apple's version](https://developer.apple.com/documentation/foundation/nsorderedset), it's pretty different. Now, let's look into how to implement apple's ordered set. +Let's look into how to implement [Ordered Set](https://developer.apple.com/documentation/foundation/nsorderedset). Here is the example about how it works @@ -450,4 +118,4 @@ public func remove(_ object: T) { } ``` -*Ordered Set is Written By Zain Humayun, Apple Ordered Set is Written By Kai Chen* +*Written By Kai Chen* diff --git a/Sorted Set/README.markdown b/Sorted Set/README.markdown new file mode 100644 index 000000000..a8b5ad1ea --- /dev/null +++ b/Sorted Set/README.markdown @@ -0,0 +1,334 @@ +# Sorted Set + +## Sorted Array Version + +An Sorted Set is a collection of unique items in sorted order. Items are usually sorted from least to greatest. + +The Sorted Set data type is a hybrid of: + +- a [Set](https://en.wikipedia.org/wiki/Set_%28mathematics%29), a collection of unique items where the order does not matter, and +- a [Sequence](https://en.wikipedia.org/wiki/Sequence), an sorted list of items where each item may appear more than once. + +It's important to keep in mind that two items can have the same *value* but still may not be equal. For example, we could define "a" and "z" to have the same value (their lengths), but clearly "a" != "z". + +## Why use an sorted set? + +Sorted Sets should be considered when you need to keep your collection sorted at all times, and you do lookups on the collection much more frequently than inserting or deleting items. Many of the lookup operations for an Sorted Set are **O(1)**. + +A good example would be keeping track of the rankings of players in a scoreboard (see example 2 below). + +#### These are sorted sets + +A set of integers: + + [1, 2, 3, 6, 8, 10, 1000] + +A set of strings: + + ["a", "is", "set", "this"] + +The "value" of these strings could be their text content, but also for example their length. + +#### These are not sorted sets + +This set violates the property of uniqueness: + + [1, 1, 2, 3, 5, 8] + +This set violates the sorted property: + + [1, 11, 2, 3] + +## The code + +We'll start by creating our internal representation for the Sorted Set. Since the idea of a set is similar to that of an array, we will use an array to represent our set. Furthermore, since we'll need to keep the set sorted, we need to be able to compare the individual elements. Thus, any type must conform to the [Comparable Protocol](https://developer.apple.com/library/watchos/documentation/Swift/Reference/Swift_Comparable_Protocol/index.html). + +```swift +public struct SortedSet { + private var internalSet = [T]() + + // Returns the number of elements in the SortedSet. + public var count: Int { + return internalSet.count + } + ... +``` + +Lets take a look at the `insert()` function first. This first checks if the item already exists in the collection. If so, it returns and does not insert the item. Otherwise, it will insert the item through straightforward iteration. + +```swift + public mutating func insert(_ item: T){ + if exists(item) { + return // don't add an item if it already exists + } + + // Insert new the item just before the one that is larger. + for i in 0.. item { + internalSet.insert(item, at: i) + return + } + } + + // Append to the back if the new item is greater than any other in the set. + internalSet.append(item) + } +``` + +As we'll see later on, checking if the item is already in the set has an efficiency of **O(log(n) + k)** where **k** is the number of items with the same value as the item we are inserting. + +To insert the new item, the `for` loop starts from the beginning of the array, and checks to see if each item is larger than the item we want to insert. Once we find such an item, we insert the new one into its place. This shifts the rest of the array over to the right by 1 position. This loop is at worst **O(n)**. + +The total performance of the `insert()` function is therefore **O(n)**. + +Next up is the `remove()` function: + +```swift + public mutating func remove(_ item: T) { + if let index = index(of: item) { + internalSet.remove(at: index) + } + } +``` + +First this checks if the item exists and then removes it from the array. Because of the `removeAtIndex()` function, the efficiency for remove is **O(n)**. + +The next function is `indexOf()`, which takes in an object of type `T` and returns the index of the corresponding item if it is in the set, or `nil` if it is not. Since our set is sorted, we can use a binary search to quickly search for the item. + +```swift + public func index(of item: T) -> Int? { + var leftBound = 0 + var rightBound = count - 1 + + while leftBound <= rightBound { + let mid = leftBound + ((rightBound - leftBound) / 2) + + if internalSet[mid] > item { + rightBound = mid - 1 + } else if internalSet[mid] < item { + leftBound = mid + 1 + } else if internalSet[mid] == item { + return mid + } else { + // see below + } + } + return nil + } +``` + +> **Note:** If you are not familiar with the concept of binary search, we have an [article that explains all about it](../Binary%20Search). + +However, there is an important issue to deal with here. Recall that two objects can be unequal yet still have the same "value" for the purposes of comparing them. Since a set can contain multiple items with the same value, it is important to check that the binary search has landed on the correct item. + +For example, consider this sorted set of `Player` objects. Each `Player` has a name and a number of points: + + [ ("Bill", 50), ("Ada", 50), ("Jony", 50), ("Steve", 200), ("Jean-Louis", 500), ("Woz", 1000) ] + +We want the set to be sorted by points, from low to high. Multiple players can have the same number of points. The name of the player is not important for this ordering. However, the name *is* important for retrieving the correct item. + +Let's say we do `indexOf(bill)` where `bill` is player object `("Bill", 50)`. If we did a traditional binary search we'd land on index 2, which is the object `("Jony", 50)`. The value 50 matches, but it's not the object we're looking for! + +Therefore, we also need to check the items with the same value to the right and left of the midpoint. The code to check the left and right side looks like this: + +```swift + // Check to the right. + for j in mid.stride(to: count - 1, by: 1) { + if internalSet[j + 1] == item { + return j + 1 + } else if internalSet[j] < internalSet[j + 1] { + break + } + } + + // Check to the left. + for j in mid.stride(to: 0, by: -1) { + if internalSet[j - 1] == item { + return j - 1 + } else if internalSet[j] > internalSet[j - 1] { + break + } + } + + return nil +``` + +These loops start at the current `mid` value and then look at the neighboring values until we've found the correct object. + +The combined runtime for `indexOf()` is **O(log(n) + k)** where **n** is the length of the set, and **k** is the number of items with the same *value* as the one that is being searched for. + +Since the set is sorted, the following operations are all **O(1)**: + +```swift + // Returns the 'maximum' or 'largest' value in the set. + public func max() -> T? { + return count == 0 ? nil : internalSet[count - 1] + } + + // Returns the 'minimum' or 'smallest' value in the set. + public func min() -> T? { + return count == 0 ? nil : internalSet[0] + } + + // Returns the k-th largest element in the set, if k is in the range + // [1, count]. Returns nil otherwise. + public func kLargest(_ k: Int) -> T? { + return k > count || k <= 0 ? nil : internalSet[count - k] + } + + // Returns the k-th smallest element in the set, if k is in the range + // [1, count]. Returns nil otherwise. + public func kSmallest(_ k: Int) -> T? { + return k > count || k <= 0 ? nil : internalSet[k - 1] + } +``` + +## Examples + +Below are a few examples that can be found in the playground file. + +### Example 1 + +Here we create a set with random Integers. Printing the largest/smallest 5 numbers in the set is fairly easy. + +```swift +// Example 1 with type Int +var mySet = SortedSet() + +// Insert random numbers into the set +for _ in 0..<50 { + mySet.insert(randomNum(50, max: 500)) +} + +print(mySet) + +print(mySet.max()) +print(mySet.min()) + +// Print the 5 largest values +for k in 1...5 { + print(mySet.kLargest(k)) +} + +// Print the 5 lowest values +for k in 1...5 { + print(mySet.kSmallest(k)) +} +``` + +### Example 2 + +In this example we take a look at something a bit more interesting. We define a `Player` struct as follows: + +```swift +public struct Player: Comparable { + public var name: String + public var points: Int +} +``` + +The `Player` also gets its own `==` and `<` operators. The `<` operator is used to determine the sort order of the set, while `==` determines whether two objects are really equal. + +Note that `==` compares both the name and the points: + +```swifr +func ==(x: Player, y: Player) -> Bool { + return x.name == y.name && x.points == y.points +} +``` + +But `<` only compares the points: + +```swift +func <(x: Player, y: Player) -> Bool { + return x.points < y.points +} +``` + +Therefore, two `Player`s can each have the same value (the number of points), but are not guaranteed to be equal (they can have different names). + +We create a new set and insert 20 random players. The `Player()` constructor gives each player a random name and score: + +```swift +var playerSet = SortedSet() + +// Populate the set with random players. +for _ in 0..<20 { + playerSet.insert(Player()) +} +``` + +Insert another player: + +```swift +var anotherPlayer = Player() +playerSet.insert(anotherPlayer) +``` + +Now we use the `indexOf()` function to find out what rank `anotherPlayer` is. + +```swift +let level = playerSet.count - playerSet.indexOf(anotherPlayer)! +print("\(anotherPlayer.name) is ranked at level \(level) with \(anotherPlayer.points) points") +``` + +### Example 3 + +The final example demonstrates the need to look for the right item even after the binary search has completed. + +We insert 9 players into the set: + +```swift +var repeatedSet = SortedSet() + +repeatedSet.insert(Player(name: "Player 1", points: 100)) +repeatedSet.insert(Player(name: "Player 2", points: 100)) +repeatedSet.insert(Player(name: "Player 3", points: 100)) +repeatedSet.insert(Player(name: "Player 4", points: 100)) +repeatedSet.insert(Player(name: "Player 5", points: 100)) +repeatedSet.insert(Player(name: "Player 6", points: 50)) +repeatedSet.insert(Player(name: "Player 7", points: 200)) +repeatedSet.insert(Player(name: "Player 8", points: 250)) +repeatedSet.insert(Player(name: "Player 9", points: 25)) +``` + +Notice how several of these players have the same value of 100 points. + +The set looks something like this: + + [Player 9, Player 6, Player 1, Player 2, Player 3, Player 4, Player 5, Player 7, Player 8] + +The next line looks for `Player 2`: + +```swift +print(repeatedSet.index(of: Player(name: "Player 2", points: 100))) +``` + +After the binary search finishes, the value of `mid` is at index 5: + + [Player 9, Player 6, Player 1, Player 2, Player 3, Player 4, Player 5, Player 7, Player 8] + mid + +However, this is not `Player 2`. Both `Player 4` and `Player 2` have the same points, but a different name. The binary search only looked at the points, not the name. + +But we do know that `Player 2` must be either to the immediate left or the right of `Player 4`, so we check both sides of `mid`. We only need to look at the objects with the same value as `Player 4`. The others are replaced by `X`: + + [X, X, Player 1, Player 2, Player 3, Player 4, Player 5, X, X] + mid + +The code then first checks on the right of `mid` (where the `*` is): + + [X, X, Player 1, Player 2, Player 3, Player 4, Player 5, X, X] + mid * + +The right side did not contain the item, so we look at the left side: + + [X, X, Player 1, Player 2, Player 3, Player 4, Player 5, X, X] + * mid + + [X, X, Player 1, Player 2, Player 3, Player 4, Player 5, X, X] + * mid + +Finally, we've found `Player 2`, and return index 3. + +*Written By Zain Humayun* diff --git a/Ordered Set/OrderedSet.playground/Pages/Example 1.xcplaygroundpage/Contents.swift b/Sorted Set/SortedSet.playground/Pages/Example 1.xcplaygroundpage/Contents.swift similarity index 94% rename from Ordered Set/OrderedSet.playground/Pages/Example 1.xcplaygroundpage/Contents.swift rename to Sorted Set/SortedSet.playground/Pages/Example 1.xcplaygroundpage/Contents.swift index c12dadf0f..45af5f99d 100644 --- a/Ordered Set/OrderedSet.playground/Pages/Example 1.xcplaygroundpage/Contents.swift +++ b/Sorted Set/SortedSet.playground/Pages/Example 1.xcplaygroundpage/Contents.swift @@ -5,7 +5,7 @@ print("Hello, Swift 4!") //: # Example 1 with type Int -var mySet = OrderedSet() +var mySet = SortedSet() // Insert random numbers into the set for _ in 0..<50 { diff --git a/Ordered Set/OrderedSet.playground/Pages/Example 1.xcplaygroundpage/timeline.xctimeline b/Sorted Set/SortedSet.playground/Pages/Example 1.xcplaygroundpage/timeline.xctimeline similarity index 50% rename from Ordered Set/OrderedSet.playground/Pages/Example 1.xcplaygroundpage/timeline.xctimeline rename to Sorted Set/SortedSet.playground/Pages/Example 1.xcplaygroundpage/timeline.xctimeline index 53114d22e..dffc42643 100644 --- a/Ordered Set/OrderedSet.playground/Pages/Example 1.xcplaygroundpage/timeline.xctimeline +++ b/Sorted Set/SortedSet.playground/Pages/Example 1.xcplaygroundpage/timeline.xctimeline @@ -3,17 +3,17 @@ version = "3.0"> diff --git a/Ordered Set/OrderedSet.playground/Pages/Example 2.xcplaygroundpage/Contents.swift b/Sorted Set/SortedSet.playground/Pages/Example 2.xcplaygroundpage/Contents.swift similarity index 95% rename from Ordered Set/OrderedSet.playground/Pages/Example 2.xcplaygroundpage/Contents.swift rename to Sorted Set/SortedSet.playground/Pages/Example 2.xcplaygroundpage/Contents.swift index c30260fff..2408d35d2 100644 --- a/Ordered Set/OrderedSet.playground/Pages/Example 2.xcplaygroundpage/Contents.swift +++ b/Sorted Set/SortedSet.playground/Pages/Example 2.xcplaygroundpage/Contents.swift @@ -2,7 +2,7 @@ //: # Example 2 with Player objects -var playerSet = OrderedSet() +var playerSet = SortedSet() // Populate the set with random players. for _ in 0..<20 { diff --git a/Ordered Set/OrderedSet.playground/Pages/Example 3.xcplaygroundpage/Contents.swift b/Sorted Set/SortedSet.playground/Pages/Example 3.xcplaygroundpage/Contents.swift similarity index 95% rename from Ordered Set/OrderedSet.playground/Pages/Example 3.xcplaygroundpage/Contents.swift rename to Sorted Set/SortedSet.playground/Pages/Example 3.xcplaygroundpage/Contents.swift index 52f154c05..63edd11d2 100644 --- a/Ordered Set/OrderedSet.playground/Pages/Example 3.xcplaygroundpage/Contents.swift +++ b/Sorted Set/SortedSet.playground/Pages/Example 3.xcplaygroundpage/Contents.swift @@ -2,7 +2,7 @@ //: # Example 3: multiple entries with the same value -var repeatedSet = OrderedSet() +var repeatedSet = SortedSet() repeatedSet.insert(Player(name: "Player 1", points: 100)) repeatedSet.insert(Player(name: "Player 2", points: 100)) diff --git a/Ordered Set/OrderedSet.playground/Sources/Player.swift b/Sorted Set/SortedSet.playground/Sources/Player.swift similarity index 95% rename from Ordered Set/OrderedSet.playground/Sources/Player.swift rename to Sorted Set/SortedSet.playground/Sources/Player.swift index 26835b0a8..0960b2400 100644 --- a/Ordered Set/OrderedSet.playground/Sources/Player.swift +++ b/Sorted Set/SortedSet.playground/Sources/Player.swift @@ -3,12 +3,12 @@ public struct Player: Comparable { public var name: String public var points: Int - + public init() { self.name = String.random() self.points = random(min: 0, max: 5000) } - + public init(name: String, points: Int) { self.name = name self.points = points @@ -31,7 +31,7 @@ public func print(player: Player) { print("Player: \(player.name) | Points: \(player.points)") } -public func print(set: OrderedSet) { +public func print(set: SortedSet) { for i in 0.. String { let base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" var randomString: String = "" - + for _ in 0.. { + private var internalSet = [T]() + + public init() { } + + // Returns the number of elements in the SortedSet. + public var count: Int { + return internalSet.count + } + + // Inserts an item. Performance: O(n) + public mutating func insert(_ item: T) { + if exists(item) { + return // don't add an item if it already exists + } + + // Insert new the item just before the one that is larger. + for i in 0.. item { + internalSet.insert(item, at: i) + return + } + } + + // Append to the back if the new item is greater than any other in the set. + internalSet.append(item) + } + + // Removes an item if it exists. Performance: O(n) + public mutating func remove(_ item: T) { + if let index = index(of: item) { + internalSet.remove(at: index) + } + } + + // Returns true if and only if the item exists somewhere in the set. + public func exists(_ item: T) -> Bool { + return index(of: item) != nil + } + + // Returns the index of an item if it exists, or -1 otherwise. + public func index(of item: T) -> Int? { + var leftBound = 0 + var rightBound = count - 1 + + while leftBound <= rightBound { + let mid = leftBound + ((rightBound - leftBound) / 2) + + if internalSet[mid] > item { + rightBound = mid - 1 + } else if internalSet[mid] < item { + leftBound = mid + 1 + } else if internalSet[mid] == item { + return mid + } else { + // When we get here, we've landed on an item whose value is equal to the + // value of the item we're looking for, but the items themselves are not + // equal. We need to check the items with the same value to the right + // and to the left in order to find an exact match. + + // Check to the right. + for j in stride(from: mid, to: count - 1, by: 1) { + if internalSet[j + 1] == item { + return j + 1 + } else if internalSet[j] < internalSet[j + 1] { + break + } + } + + // Check to the left. + for j in stride(from: mid, to: 0, by: -1) { + if internalSet[j - 1] == item { + return j - 1 + } else if internalSet[j] > internalSet[j - 1] { + break + } + } + return nil + } + } + return nil + } + + // Returns the item at the given index. + // Assertion fails if the index is out of the range of [0, count). + public subscript(index: Int) -> T { + assert(index >= 0 && index < count) + return internalSet[index] + } + + // Returns the 'maximum' or 'largest' value in the set. + public func max() -> T? { + return count == 0 ? nil : internalSet[count - 1] + } + + // Returns the 'minimum' or 'smallest' value in the set. + public func min() -> T? { + return count == 0 ? nil : internalSet[0] + } + + // Returns the k-th largest element in the set, if k is in the range + // [1, count]. Returns nil otherwise. + public func kLargest(_ k: Int) -> T? { + return k > count || k <= 0 ? nil : internalSet[count - k] + } + + // Returns the k-th smallest element in the set, if k is in the range + // [1, count]. Returns nil otherwise. + public func kSmallest(_ k: Int) -> T? { + return k > count || k <= 0 ? nil : internalSet[k - 1] + } +} + diff --git a/Sorted Set/SortedSet.playground/contents.xcplayground b/Sorted Set/SortedSet.playground/contents.xcplayground new file mode 100644 index 000000000..18c02c912 --- /dev/null +++ b/Sorted Set/SortedSet.playground/contents.xcplayground @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/Ordered Set/OrderedSet.playground/playground.xcworkspace/contents.xcworkspacedata b/Sorted Set/SortedSet.playground/playground.xcworkspace/contents.xcworkspacedata similarity index 100% rename from Ordered Set/OrderedSet.playground/playground.xcworkspace/contents.xcworkspacedata rename to Sorted Set/SortedSet.playground/playground.xcworkspace/contents.xcworkspacedata diff --git a/Sorted Set/SortedSet.swift b/Sorted Set/SortedSet.swift new file mode 100644 index 000000000..69546da0d --- /dev/null +++ b/Sorted Set/SortedSet.swift @@ -0,0 +1,118 @@ +/* + An Sorted Set is a collection where all items in the set follow an ordering, + usually sorted from 'least' to 'most'. The way you value and compare items + can be user-defined. + */ +public struct SortedSet { + private var internalSet = [T]() + + public init() { } + + // Returns the number of elements in the SortedSet. + public var count: Int { + return internalSet.count + } + + // Inserts an item. Performance: O(n) + public mutating func insert(_ item: T) { + if exists(item) { + return // don't add an item if it already exists + } + + // Insert new the item just before the one that is larger. + for i in 0.. item { + internalSet.insert(item, at: i) + return + } + } + + // Append to the back if the new item is greater than any other in the set. + internalSet.append(item) + } + + // Removes an item if it exists. Performance: O(n) + public mutating func remove(_ item: T) { + if let index = index(of: item) { + internalSet.remove(at: index) + } + } + + // Returns true if and only if the item exists somewhere in the set. + public func exists(_ item: T) -> Bool { + return index(of: item) != nil + } + + // Returns the index of an item if it exists, or -1 otherwise. + public func index(of item: T) -> Int? { + var leftBound = 0 + var rightBound = count - 1 + + while leftBound <= rightBound { + let mid = leftBound + ((rightBound - leftBound) / 2) + + if internalSet[mid] > item { + rightBound = mid - 1 + } else if internalSet[mid] < item { + leftBound = mid + 1 + } else if internalSet[mid] == item { + return mid + } else { + // When we get here, we've landed on an item whose value is equal to the + // value of the item we're looking for, but the items themselves are not + // equal. We need to check the items with the same value to the right + // and to the left in order to find an exact match. + + // Check to the right. + for j in stride(from: mid, to: count - 1, by: 1) { + if internalSet[j + 1] == item { + return j + 1 + } else if internalSet[j] < internalSet[j + 1] { + break + } + } + + // Check to the left. + for j in stride(from: mid, to: 0, by: -1) { + if internalSet[j - 1] == item { + return j - 1 + } else if internalSet[j] > internalSet[j - 1] { + break + } + } + return nil + } + } + return nil + } + + // Returns the item at the given index. + // Assertion fails if the index is out of the range of [0, count). + public subscript(index: Int) -> T { + assert(index >= 0 && index < count) + return internalSet[index] + } + + // Returns the 'maximum' or 'largest' value in the set. + public func max() -> T? { + return count == 0 ? nil : internalSet[count - 1] + } + + // Returns the 'minimum' or 'smallest' value in the set. + public func min() -> T? { + return count == 0 ? nil : internalSet[0] + } + + // Returns the k-th largest element in the set, if k is in the range + // [1, count]. Returns nil otherwise. + public func kLargest(_ k: Int) -> T? { + return k > count || k <= 0 ? nil : internalSet[count - k] + } + + // Returns the k-th smallest element in the set, if k is in the range + // [1, count]. Returns nil otherwise. + public func kSmallest(_ k: Int) -> T? { + return k > count || k <= 0 ? nil : internalSet[k - 1] + } +} + diff --git a/swift-algorithm-club.xcworkspace/contents.xcworkspacedata b/swift-algorithm-club.xcworkspace/contents.xcworkspacedata index fbaa4cd54..3c7134d72 100644 --- a/swift-algorithm-club.xcworkspace/contents.xcworkspacedata +++ b/swift-algorithm-club.xcworkspace/contents.xcworkspacedata @@ -1,6 +1,19 @@ + + + + + + + + @@ -1807,12 +1820,6 @@ - - - -