Skip to content

Commit 5bdea96

Browse files
committed
Merge remote-tracking branch 'origin/fix-algo' into docdocs
2 parents f674851 + 62ee6f2 commit 5bdea96

File tree

2 files changed

+67
-113
lines changed

2 files changed

+67
-113
lines changed

Sources/Algorithms/PartialSort.swift

Lines changed: 34 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -7,60 +7,46 @@
77
//
88
// See https://swift.org/LICENSE.txt for license information
99
//
10+
11+
//===----------------------------------------------------------------------===//
12+
// sortedPrefix(_:by:)
1013
//===----------------------------------------------------------------------===//
1114

12-
extension Sequence {
13-
/// Returns the first k elements of this collection when it's sorted using
14-
/// the given predicate as the comparison between elements.
15-
///
16-
/// This example partially sorts an array of integers to retrieve its three
17-
/// smallest values:
18-
///
19-
/// let numbers = [7,1,6,2,8,3,9]
20-
/// let smallestThree = numbers.sortedPrefix(3, <)
21-
/// // [1, 2, 3]
22-
///
23-
/// If you need to sort a collection but only need access to a prefix of its
24-
/// elements, using this method can give you a performance boost over sorting
25-
/// the entire collection. The order of equal elements is guaranteed to be
26-
/// preserved.
27-
///
28-
/// - Parameter count: The k number of elements to partially sort.
29-
/// - Parameter areInIncreasingOrder: A predicate that returns true if its
30-
/// first argument should be ordered before its second argument;
31-
/// otherwise, false.
32-
///
33-
/// - Complexity: O(k log k + nk)
34-
public func partiallySorted(
15+
extension Collection {
16+
17+
public func sortedPrefix(
3518
_ count: Int,
3619
by areInIncreasingOrder: (Element, Element) throws -> Bool
37-
) rethrows -> [Element] {
38-
var result = ContiguousArray(self)
39-
try result.partiallySort(count, by: areInIncreasingOrder)
40-
return Array(result)
20+
) rethrows -> [Self.Element] {
21+
assert(count >= 0, """
22+
Cannot prefix with a negative amount of elements!
23+
"""
24+
)
25+
assert(count <= self.count, """
26+
Cannot prefix more than this Collection's size!
27+
"""
28+
)
29+
30+
// If we're attempting to prefix more than 10% of the collection, it's faster to sort everything.
31+
guard count < (self.count / 10) else {
32+
return Array(try sorted(by: areInIncreasingOrder).prefix(count))
33+
}
34+
35+
var result = try self.prefix(count).sorted(by: areInIncreasingOrder)
36+
for e in self.dropFirst(count) {
37+
if let last = result.last, try areInIncreasingOrder(last, e) { continue }
38+
if let insertionIndex = try result.firstIndex (where: { try areInIncreasingOrder(e, $0) }) {
39+
result.insert(e, at: insertionIndex)
40+
result.removeLast()
41+
}
42+
}
43+
return result
4144
}
4245
}
4346

44-
extension Sequence where Element: Comparable {
45-
/// Returns the first k elements of this collection when it's sorted.
46-
///
47-
/// This example partially sorts an array of integers to retrieve its three
48-
/// smallest values:
49-
///
50-
/// let numbers = [7,1,6,2,8,3,9]
51-
/// let smallestThree = numbers.sortedPrefix(<)
52-
/// // [1, 2, 3]
53-
///
54-
/// If you need to sort a sequence but only need access to a prefix of its
55-
/// elements, using this method can give you a performance boost over sorting
56-
/// the entire collection. The order of equal elements is guaranteed to be
57-
/// preserved.
58-
///
59-
/// - Parameter count: The k number of elements to partially sort
60-
/// in ascending order.
61-
///
62-
/// - Complexity: O(k log k + nk)
63-
public func partiallySorted(_ count: Int) -> [Element] {
64-
return partiallySorted(count, by: <)
47+
extension Collection where Element: Comparable {
48+
49+
public func sortedPrefix(_ count: Int) -> [Element] {
50+
return sortedPrefix(count, by: <)
6551
}
6652
}

Tests/SwiftAlgorithmsTests/PartialSortTests.swift

Lines changed: 33 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -15,109 +15,77 @@ import Algorithms
1515
final class PartialSortTests: XCTestCase {
1616
func testEmpty() {
1717
let array = [Int]()
18-
XCTAssertEqual(array.partiallySorted(0), [])
18+
XCTAssertEqual(array.sortedPrefix(0), [])
1919
}
2020

21-
func testPartialSortWithPriority() {
21+
func testSortedPrefixWithOrdering() {
2222
let array: [Int] = [20, 1, 4, 70, 100, 2, 3, 7, 90]
2323

24-
XCTAssertEqual(array.partiallySorted(0, by: >), array)
24+
XCTAssertEqual(array.sortedPrefix(0, by: >), [])
2525
XCTAssertEqual(
26-
array.partiallySorted(1, by: >),
27-
[100, 1, 4, 3, 7, 20, 70, 90, 2]
26+
array.sortedPrefix(1, by: >),
27+
[100]
2828
)
2929

3030
XCTAssertEqual(
31-
array.partiallySorted(5, by: >),
32-
[100, 90, 70, 20, 7, 2, 4, 3, 1]
31+
array.sortedPrefix(5, by: >),
32+
[100, 90, 70, 20, 7]
3333
)
3434

3535
XCTAssertEqual(
36-
array.partiallySorted(9, by: >),
36+
array.sortedPrefix(9, by: >),
3737
[100, 90, 70, 20, 7, 4, 3, 2, 1]
3838
)
3939

40-
XCTAssertEqual([1].partiallySorted(0, by: <), [1])
41-
XCTAssertEqual([1].partiallySorted(0, by: >), [1])
42-
XCTAssertEqual([1].partiallySorted(1, by: <), [1])
43-
XCTAssertEqual([1].partiallySorted(1, by: >), [1])
44-
XCTAssertEqual([0, 1].partiallySorted(1, by: <), [0, 1])
45-
XCTAssertEqual([1, 0].partiallySorted(1, by: <), [0, 1])
46-
XCTAssertEqual([1, 0].partiallySorted(2, by: <), [0, 1])
47-
XCTAssertEqual([0, 1].partiallySorted(1, by: >), [1, 0])
48-
XCTAssertEqual([1, 0].partiallySorted(1, by: >), [1, 0])
49-
XCTAssertEqual([1, 0].partiallySorted(2, by: >), [1, 0])
40+
XCTAssertEqual([1].sortedPrefix(0, by: <), [])
41+
XCTAssertEqual([1].sortedPrefix(0, by: >), [])
42+
XCTAssertEqual([1].sortedPrefix(1, by: <), [1])
43+
XCTAssertEqual([1].sortedPrefix(1, by: >), [1])
44+
XCTAssertEqual([0, 1].sortedPrefix(1, by: <), [0])
45+
XCTAssertEqual([1, 0].sortedPrefix(1, by: <), [0])
46+
XCTAssertEqual([1, 0].sortedPrefix(2, by: <), [0, 1])
47+
XCTAssertEqual([0, 1].sortedPrefix(1, by: >), [1])
48+
XCTAssertEqual([1, 0].sortedPrefix(1, by: >), [1])
49+
XCTAssertEqual([1, 0].sortedPrefix(2, by: >), [1, 0])
5050

5151
XCTAssertEqual(
52-
[1, 2, 3, 4, 7, 20, 70, 90, 100].partiallySorted(5, by: <),
53-
[1, 2, 3, 4, 7, 90, 70, 20, 100]
52+
[1, 2, 3, 4, 7, 20, 70, 90, 100].sortedPrefix(5, by: <),
53+
[1, 2, 3, 4, 7]
5454
)
5555

5656
XCTAssertEqual(
57-
[1, 2, 3, 4, 7, 20, 70, 90, 100].partiallySorted(5, by: >),
58-
[100, 90, 70, 20, 7, 2, 4, 3, 1]
57+
[1, 2, 3, 4, 7, 20, 70, 90, 100].sortedPrefix(5, by: >),
58+
[100, 90, 70, 20, 7]
5959
)
6060

6161
XCTAssertEqual(
62-
[1, 2, 3, 4, 7, 20, 70, 90, 100].partiallySorted(5, by: >),
63-
[100, 90, 70, 20, 7, 2, 4, 3, 1]
62+
[1, 2, 3, 4, 7, 20, 70, 90, 100].sortedPrefix(5, by: >),
63+
[100, 90, 70, 20, 7]
6464
)
6565

6666
XCTAssertEqual(
67-
[1, 2, 3, 4, 7, 20, 70, 90, 100].partiallySorted(5, by: <),
68-
[1, 2, 3, 4, 7, 90, 70, 20, 100]
67+
[1, 2, 3, 4, 7, 20, 70, 90, 100].sortedPrefix(5, by: <),
68+
[1, 2, 3, 4, 7]
6969
)
7070
}
7171

72-
func testPartialSortComparable() {
72+
func testSortedPrefixComparable() {
7373
let array: [Int] = [20, 1, 4, 70, 100, 2, 3, 7, 90]
7474

75-
XCTAssertEqual(array.partiallySorted(0), array)
75+
XCTAssertEqual(array.sortedPrefix(0), [])
7676

7777
XCTAssertEqual(
78-
array.partiallySorted(1),
79-
[1, 90, 4, 70, 100, 7, 3, 2, 20]
78+
array.sortedPrefix(1),
79+
[1]
8080
)
8181

8282
XCTAssertEqual(
83-
array.partiallySorted(5),
84-
[1, 2, 3, 4, 7, 90, 70, 20, 100]
83+
array.sortedPrefix(5),
84+
[1, 2, 3, 4, 7]
8585
)
8686

8787
XCTAssertEqual(
88-
array.partiallySorted(9),
89-
[1, 2, 3, 4, 7, 20, 70, 90, 100]
90-
)
91-
}
92-
93-
func testPartialSortInPlaceComparable() {
94-
let originalArray: [Int] = [20, 1, 4, 70, 100, 2, 3, 7, 90]
95-
var array = originalArray
96-
97-
array.partiallySort(0)
98-
XCTAssertEqual(array, originalArray)
99-
100-
array = originalArray
101-
102-
array.partiallySort(1)
103-
XCTAssertEqual(
104-
array,
105-
[1, 90, 4, 70, 100, 7, 3, 2, 20]
106-
)
107-
108-
array = originalArray
109-
110-
array.partiallySort(5)
111-
XCTAssertEqual(
112-
array,
113-
[1, 2, 3, 4, 7, 90, 70, 20, 100]
114-
)
115-
116-
array = originalArray
117-
118-
array.partiallySort(9)
119-
XCTAssertEqual(
120-
array,
88+
array.sortedPrefix(9),
12189
[1, 2, 3, 4, 7, 20, 70, 90, 100]
12290
)
12391
}

0 commit comments

Comments
 (0)