Skip to content

Commit 5429d3b

Browse files
committed
Add partial sort algorithm
1 parent 7ab0a20 commit 5429d3b

File tree

1 file changed

+89
-0
lines changed

1 file changed

+89
-0
lines changed

Sources/Algorithms/PartialSort.swift

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
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

Comments
 (0)