Skip to content

Commit 63b2dd0

Browse files
authored
Merge pull request #1 from rakaramos/guide
Guide docs
2 parents 4362197 + 6cd2870 commit 63b2dd0

File tree

3 files changed

+75
-10
lines changed

3 files changed

+75
-10
lines changed

Guides/PartialSort.md

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Partial Sort
2+
3+
[[Source](https://github.com/apple/swift-algorithms/blob/main/Sources/Algorithms/PartialSort.swift) |
4+
[Tests](https://github.com/apple/swift-algorithms/blob/main/Tests/SwiftAlgorithmsTests/PartialSortTests.swift)]
5+
6+
Returns a collection such that the `0...k` range contains the first k sorted elements of a sequence.
7+
The order of equal elements is not guaranteed to be preserved, and the order of the remaining elements is unspecified.
8+
9+
If you need to sort a sequence but only need access to a prefix of its elements,
10+
using this method can give you a performance boost over sorting the entire sequence.
11+
12+
```swift
13+
let numbers = [7,1,6,2,8,3,9]
14+
let almostSorted = numbers.partiallySorted(3, <)
15+
// [1, 2, 3, 9, 7, 6, 8]
16+
```
17+
18+
## Detailed Design
19+
20+
This adds the in place `MutableCollection` method shown below:
21+
22+
```swift
23+
extension Sequence {
24+
func partiallySort(_ count: Int, by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows
25+
}
26+
```
27+
28+
Additionally, versions of this method that return a new array and abstractions for `Comparable` types are also provided:
29+
30+
```swift
31+
extension MutableCollection where Self: RandomAccessCollection, Element: Comparable {
32+
public mutating func partiallySort(_ count: Int)
33+
}
34+
35+
extension Sequence {
36+
public func partiallySorted(_ count: Int, by areInIncreasingOrder: (Element, Element) throws -> Bool) rethrows -> [Element]
37+
}
38+
39+
extension Sequence where Element: Comparable {
40+
public func partiallySorted(_ count: Int) -> [Element]
41+
}
42+
```
43+
44+
### Complexity
45+
46+
Partially sorting is a O(_k log n_) operation, where _k_ is the number of elements to sort
47+
and _n_ is the length of the sequence.
48+
49+
`partiallySort(_:by:)` is a slight generalization of a priority queue. It's implemented
50+
as an in line heapsort that stops after _k_ runs.
51+
52+
### Comparison with other languages
53+
54+
**C++:** The `<algorithm>` library defines a `partial_sort` function with similar
55+
semantics to this one.
56+
57+
**Python:** Defines a `heapq` priority queue that can be used to manually
58+
achieve the same result.
59+

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ Read more about the package, and the intent behind it, in the [announcement on s
2828
- [`randomStableSample(count:)`, `randomStableSample(count:using:)`](https://github.com/apple/swift-algorithms/blob/main/Guides/RandomSampling.md): Randomly selects a specific number of elements from a collection, preserving their original relative order.
2929
- [`uniqued()`, `uniqued(on:)`](https://github.com/apple/swift-algorithms/blob/main/Guides/Unique.md): The unique elements of a collection, preserving their order.
3030

31+
#### Partial sorting
32+
33+
- [`partiallySorted(_:by:)`](https://github.com/apple/swift-algorithms/blob/main/Guides/PartialSort.md): Sorts a sequence only up to a specific index, leaving the remaining elements unsorted.
34+
3135
#### Other useful operations
3236

3337
- [`chunked(by:)`, `chunked(on:)`](https://github.com/apple/swift-algorithms/blob/main/Guides/Chunked.md): Eager and lazy operations that break a collection into chunks based on either a binary predicate or when the result of a projection changes.

Sources/Algorithms/PartialSort.swift

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ extension Sequence where Element: Comparable {
7070
}
7171
}
7272

73-
extension MutableCollection where Self: RandomAccessCollection, Index == Int {
73+
extension MutableCollection where Self: RandomAccessCollection {
7474
/// Rearranges this collection such that the 0...k range contains the first
7575
/// k sorted elements in this collection, using the given predicate as the
7676
/// comparison between elements.
@@ -101,7 +101,7 @@ extension MutableCollection where Self: RandomAccessCollection, Index == Int {
101101
}
102102
}
103103

104-
extension MutableCollection where Self: RandomAccessCollection, Element: Comparable, Index == Int {
104+
extension MutableCollection where Self: RandomAccessCollection, Element: Comparable {
105105
/// Rearranges this collection such that the 0...k range contains the first
106106
/// k smallest elements in this collection.
107107
///
@@ -133,11 +133,11 @@ extension MutableCollection where Self: RandomAccessCollection, Element: Compara
133133
// __partiallySort(_:by:)
134134
//===----------------------------------------------------------------------===//
135135

136-
extension MutableCollection where Self: RandomAccessCollection, Index == Int {
136+
extension MutableCollection where Self: RandomAccessCollection {
137137
typealias Priority = (Element, Element) throws -> Bool
138138

139-
/// Partially sorts this array by using an in place heapsort that stops after we find the desired k amount
140-
/// of elements. The heap is stored and processed in reverse order so that the array doesn't have to be flipped
139+
/// Partially sorts this collection by using an in place heapsort that stops after we find the desired k amount
140+
/// of elements. The heap is stored and processed in reverse order so that the collection doesn't have to be flipped
141141
/// once the final result is found.
142142
///
143143
/// Complexity: O(k log n)
@@ -154,17 +154,17 @@ extension MutableCollection where Self: RandomAccessCollection, Index == Int {
154154
}
155155
var iterator = (0..<k).makeIterator()
156156
_ = iterator.next()
157-
swapAt(count - 1, heapEndIndex)
157+
swapAt(index(before: endIndex), index(startIndex, offsetBy: heapEndIndex))
158158
heapEndIndex += 1
159159
while let _ = iterator.next() {
160160
try siftDown(count - 1, by: areInIncreasingOrder, heapEndIndex: heapEndIndex)
161-
swapAt(count - 1, heapEndIndex)
161+
swapAt(index(before: endIndex), index(startIndex, offsetBy: heapEndIndex))
162162
heapEndIndex += 1
163163
}
164164
}
165165

166166
/// Sifts down an element from this heap.
167-
/// The heap is stored in reverse order, so sifting down will actually move the element up in the heap array.
167+
/// The heap is stored in reverse order, so sifting down will actually move the element up in the heap.
168168
///
169169
/// - Parameter i: The element index to sift down
170170
/// - Parameter by: The predicate to use when determining the priority of elements in the heap
@@ -174,7 +174,7 @@ extension MutableCollection where Self: RandomAccessCollection, Index == Int {
174174
guard indexToSwap != i else {
175175
return
176176
}
177-
swapAt(i, indexToSwap)
177+
swapAt(index(startIndex, offsetBy: i), index(startIndex, offsetBy: indexToSwap))
178178
try siftDown(indexToSwap, by: priority, heapEndIndex: heapEndIndex)
179179
}
180180

@@ -199,7 +199,9 @@ extension MutableCollection where Self: RandomAccessCollection, Index == Int {
199199
guard child >= heapEndIndex else {
200200
return parent
201201
}
202-
guard try priority(self[child], self[parent]) else {
202+
let childElement = self[index(startIndex, offsetBy: child)]
203+
let parentElement = self[index(startIndex, offsetBy: parent)]
204+
guard try priority(childElement, parentElement) else {
203205
return parent
204206
}
205207
return child

0 commit comments

Comments
 (0)