|
| 1 | +import Foundation |
| 2 | + |
| 3 | +private final class Heap<T> { |
| 4 | + |
| 5 | + typealias Comparator = (T,T) -> Bool |
| 6 | + |
| 7 | + private var elements: [T] |
| 8 | + private let priority: Comparator |
| 9 | + |
| 10 | + init<S: Sequence>(elements: S, priority: @escaping Comparator) where S.Element == T { |
| 11 | + self.priority = priority |
| 12 | + self.elements = Array(elements) |
| 13 | + if elements.isEmpty == false { |
| 14 | + for i in stride(from: (count / 2) - 1, to: -1, by: -1) { |
| 15 | + siftDown(i) |
| 16 | + } |
| 17 | + } |
| 18 | + } |
| 19 | + |
| 20 | + private func leftChildIndex(of index: Int) -> Int { |
| 21 | + return (2 * index) + 1 |
| 22 | + } |
| 23 | + |
| 24 | + private func rightChild(of index: Int) -> Int { |
| 25 | + return (2 * index) + 2 |
| 26 | + } |
| 27 | + |
| 28 | + private func parentIndex(of index: Int) -> Int { |
| 29 | + return (index - 1) / 2 |
| 30 | + } |
| 31 | + |
| 32 | + private func isHigherPriority(_ a: Int, _ b: Int) -> Bool { |
| 33 | + return priority(elements[a], elements[b]) |
| 34 | + } |
| 35 | + |
| 36 | + private func highestPriorityIndex(of index: Int) -> Int { |
| 37 | + let left = highestPriorityIndex(of: index, and: leftChildIndex(of: index)) |
| 38 | + let right = highestPriorityIndex(of: index, and: rightChild(of: index)) |
| 39 | + return highestPriorityIndex(of: left, and: right) |
| 40 | + } |
| 41 | + |
| 42 | + private func highestPriorityIndex(of parent: Int, and child: Int) -> Int { |
| 43 | + guard child < elements.count else { |
| 44 | + return parent |
| 45 | + } |
| 46 | + guard isHigherPriority(child, parent) else { |
| 47 | + return parent |
| 48 | + } |
| 49 | + return child |
| 50 | + } |
| 51 | + |
| 52 | + func dequeue() -> T? { |
| 53 | + guard elements.count > 0 else { |
| 54 | + return nil |
| 55 | + } |
| 56 | + elements.swapAt(0, elements.count - 1) |
| 57 | + let element = elements.popLast() |
| 58 | + siftDown(0) |
| 59 | + return element |
| 60 | + } |
| 61 | + |
| 62 | + private func siftDown(_ i: Int) { |
| 63 | + let indexToSwap = highestPriorityIndex(of: i) |
| 64 | + guard indexToSwap != i else { |
| 65 | + return |
| 66 | + } |
| 67 | + elements.swapAt(indexToSwap, i) |
| 68 | + siftDown(indexToSwap) |
| 69 | + } |
| 70 | +} |
| 71 | + |
| 72 | +extension Collection { |
| 73 | + func partiallySorted(_ count: Int, by: @escaping (Element, Element) -> Bool) -> [Element] { |
| 74 | + assert(count >= 0 && count < self.count, "Are you crazy?") |
| 75 | + let heap = Heap<Element>(elements: self, priority: by) |
| 76 | + return [Element](unsafeUninitializedCapacity: count) { buffer, initializedCount in |
| 77 | + for i in 0..<count { |
| 78 | + buffer[i] = heap.dequeue()! |
| 79 | + } |
| 80 | + initializedCount = count |
| 81 | + } |
| 82 | + } |
| 83 | +} |
| 84 | + |
| 85 | +extension Collection where Element: Comparable { |
| 86 | + func partiallySorted(_ count: Int) -> [Element] { |
| 87 | + return partiallySorted(count, by: <) |
| 88 | + } |
| 89 | +} |
0 commit comments