Skip to content

Commit 04b8af5

Browse files
QuickSort.swift
1 parent 2ad2dec commit 04b8af5

1 file changed

Lines changed: 206 additions & 0 deletions

File tree

QuickSort/Swift/QuickSort.swift

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
import Foundation
2+
3+
/*
4+
Easy to understand but not very efficient.
5+
You can find more swift algorithms on https://github.com/raywenderlich/swift-algorithm-club
6+
*/
7+
func quicksort<T: Comparable>(_ a: [T]) -> [T] {
8+
guard a.count > 1 else { return a }
9+
10+
let pivot = a[a.count/2]
11+
let less = a.filter { $0 < pivot }
12+
let equal = a.filter { $0 == pivot }
13+
let greater = a.filter { $0 > pivot }
14+
15+
return quicksort(less) + equal + quicksort(greater)
16+
}
17+
18+
// MARK: - Lomuto
19+
20+
/*
21+
Lomuto's partitioning algorithm.
22+
23+
This is conceptually simpler than Hoare's original scheme but less efficient.
24+
25+
The return value is the index of the pivot element in the new array. The left
26+
partition is [low...p-1]; the right partition is [p+1...high], where p is the
27+
return value.
28+
29+
The left partition includes all values smaller than or equal to the pivot, so
30+
if the pivot value occurs more than once, its duplicates will be found in the
31+
left partition.
32+
*/
33+
func partitionLomuto<T: Comparable>(_ a: inout [T], low: Int, high: Int) -> Int {
34+
// We always use the highest item as the pivot.
35+
let pivot = a[high]
36+
37+
// This loop partitions the array into four (possibly empty) regions:
38+
// [low ... i] contains all values <= pivot,
39+
// [i+1 ... j-1] contains all values > pivot,
40+
// [j ... high-1] are values we haven't looked at yet,
41+
// [high ] is the pivot value.
42+
var i = low
43+
for j in low..<high {
44+
if a[j] <= pivot {
45+
(a[i], a[j]) = (a[j], a[i])
46+
i += 1
47+
}
48+
}
49+
50+
// Swap the pivot element with the first element that is greater than
51+
// the pivot. Now the pivot sits between the <= and > regions and the
52+
// array is properly partitioned.
53+
(a[i], a[high]) = (a[high], a[i])
54+
return i
55+
}
56+
57+
/*
58+
Recursive, in-place version that uses Lomuto's partioning scheme.
59+
*/
60+
func quicksortLomuto<T: Comparable>(_ a: inout [T], low: Int, high: Int) {
61+
if low < high {
62+
let p = partitionLomuto(&a, low: low, high: high)
63+
quicksortLomuto(&a, low: low, high: p - 1)
64+
quicksortLomuto(&a, low: p + 1, high: high)
65+
}
66+
}
67+
68+
// MARK: - Hoare partitioning
69+
70+
/*
71+
Hoare's partitioning scheme.
72+
73+
The return value is NOT necessarily the index of the pivot element in the
74+
new array. Instead, the array is partitioned into [low...p] and [p+1...high],
75+
where p is the return value. The pivot value is placed somewhere inside one
76+
of the two partitions, but the algorithm doesn't tell you which one or where.
77+
78+
If the pivot value occurs more than once, then some instances may appear in
79+
the left partition and others may appear in the right partition.
80+
81+
Hoare scheme is more efficient than Lomuto's partition scheme; it performs
82+
fewer swaps.
83+
*/
84+
func partitionHoare<T: Comparable>(_ a: inout [T], low: Int, high: Int) -> Int {
85+
let pivot = a[low]
86+
var i = low - 1
87+
var j = high + 1
88+
89+
while true {
90+
repeat { j -= 1 } while a[j] > pivot
91+
repeat { i += 1 } while a[i] < pivot
92+
93+
if i < j {
94+
a.swapAt(i, j)
95+
} else {
96+
return j
97+
}
98+
}
99+
}
100+
101+
/*
102+
Recursive, in-place version that uses Hoare's partioning scheme. Because of
103+
the choice of pivot, this performs badly if the array is already sorted.
104+
*/
105+
func quicksortHoare<T: Comparable>(_ a: inout [T], low: Int, high: Int) {
106+
if low < high {
107+
let p = partitionHoare(&a, low: low, high: high)
108+
quicksortHoare(&a, low: low, high: p)
109+
quicksortHoare(&a, low: p + 1, high: high)
110+
}
111+
}
112+
113+
// MARK: - Randomized sort
114+
115+
/* Returns a random integer in the range min...max, inclusive. */
116+
public func random(min: Int, max: Int) -> Int {
117+
assert(min < max)
118+
return min + Int(arc4random_uniform(UInt32(max - min + 1)))
119+
}
120+
121+
/*
122+
Uses a random pivot index. On average, this results in a well-balanced split
123+
of the input array.
124+
*/
125+
func quicksortRandom<T: Comparable>(_ a: inout [T], low: Int, high: Int) {
126+
if low < high {
127+
// Create a random pivot index in the range [low...high].
128+
let pivotIndex = random(min: low, max: high)
129+
130+
// Because the Lomuto scheme expects a[high] to be the pivot entry, swap
131+
// a[pivotIndex] with a[high] to put the pivot element at the end.
132+
(a[pivotIndex], a[high]) = (a[high], a[pivotIndex])
133+
134+
let p = partitionLomuto(&a, low: low, high: high)
135+
quicksortRandom(&a, low: low, high: p - 1)
136+
quicksortRandom(&a, low: p + 1, high: high)
137+
}
138+
}
139+
140+
// MARK: - Dutch national flag partitioning
141+
142+
/*
143+
Swift's swap() doesn't like it if the items you're trying to swap refer to
144+
the same memory location. This little wrapper simply ignores such swaps.
145+
*/
146+
public func swap<T>(_ a: inout [T], _ i: Int, _ j: Int) {
147+
if i != j {
148+
a.swapAt(i, j)
149+
}
150+
}
151+
152+
/*
153+
Dutch national flag partitioning
154+
155+
Partitions the array into three sections: all element smaller than the pivot,
156+
all elements equal to the pivot, and all larger elements.
157+
158+
This makes for a more efficient Quicksort if the array contains many duplicate
159+
elements.
160+
161+
Returns a tuple with the start and end index of the middle area. For example,
162+
on [0,1,2,3,3,3,4,5] it returns (3, 5). Note: These indices are relative to 0,
163+
not to "low"!
164+
165+
The number of occurrences of the pivot is: result.1 - result.0 + 1
166+
167+
Time complexity is O(n), space complexity is O(1).
168+
*/
169+
func partitionDutchFlag<T: Comparable>(_ a: inout [T], low: Int, high: Int, pivotIndex: Int) -> (Int, Int) {
170+
let pivot = a[pivotIndex]
171+
172+
var smaller = low
173+
var equal = low
174+
var larger = high
175+
176+
// This loop partitions the array into four (possibly empty) regions:
177+
// [low ...smaller-1] contains all values < pivot,
178+
// [smaller... equal-1] contains all values == pivot,
179+
// [equal ... larger] contains all values > pivot,
180+
// [larger ... high] are values we haven't looked at yet.
181+
while equal <= larger {
182+
if a[equal] < pivot {
183+
swap(&a, smaller, equal)
184+
smaller += 1
185+
equal += 1
186+
} else if a[equal] == pivot {
187+
equal += 1
188+
} else {
189+
swap(&a, equal, larger)
190+
larger -= 1
191+
}
192+
}
193+
return (smaller, larger)
194+
}
195+
196+
/*
197+
Uses Dutch national flag partitioning and a random pivot index.
198+
*/
199+
func quicksortDutchFlag<T: Comparable>(_ a: inout [T], low: Int, high: Int) {
200+
if low < high {
201+
let pivotIndex = random(min: low, max: high)
202+
let (p, q) = partitionDutchFlag(&a, low: low, high: high, pivotIndex: pivotIndex)
203+
quicksortDutchFlag(&a, low: low, high: p - 1)
204+
quicksortDutchFlag(&a, low: q + 1, high: high)
205+
}
206+
}

0 commit comments

Comments
 (0)