forked from kodecocodes/swift-algorithm-club
-
Notifications
You must be signed in to change notification settings - Fork 0
/
MergeSort.swift
111 lines (95 loc) · 2.76 KB
/
MergeSort.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
//
// Mergesort.swift
//
//
// Created by Kelvin Lau on 2016-02-03.
//
//
func mergeSort<T: Comparable>(_ array: [T]) -> [T] {
guard array.count > 1 else { return array }
let middleIndex = array.count / 2
let leftArray = mergeSort(Array(array[0..<middleIndex]))
let rightArray = mergeSort(Array(array[middleIndex..<array.count]))
return merge(leftPile: leftArray, rightPile: rightArray)
}
func merge<T: Comparable>(leftPile: [T], rightPile: [T]) -> [T] {
var leftIndex = 0
var rightIndex = 0
var orderedPile = [T]()
if orderedPile.capacity < leftPile.count + rightPile.count {
orderedPile.reserveCapacity(leftPile.count + rightPile.count)
}
while leftIndex < leftPile.count && rightIndex < rightPile.count {
if leftPile[leftIndex] < rightPile[rightIndex] {
orderedPile.append(leftPile[leftIndex])
leftIndex += 1
} else if leftPile[leftIndex] > rightPile[rightIndex] {
orderedPile.append(rightPile[rightIndex])
rightIndex += 1
} else {
orderedPile.append(leftPile[leftIndex])
leftIndex += 1
orderedPile.append(rightPile[rightIndex])
rightIndex += 1
}
}
while leftIndex < leftPile.count {
orderedPile.append(leftPile[leftIndex])
leftIndex += 1
}
while rightIndex < rightPile.count {
orderedPile.append(rightPile[rightIndex])
rightIndex += 1
}
return orderedPile
}
/*
This is an iterative bottom-up implementation. Instead of recursively splitting
up the array into smaller sublists, it immediately starts merging the individual
array elements.
As the algorithm works its way up, it no longer merges individual elements but
larger and larger subarrays, until eventually the entire array is merged and
sorted.
To avoid allocating many temporary array objects, it uses double-buffering with
just two arrays.
*/
func mergeSortBottomUp<T>(_ a: [T], _ isOrderedBefore: (T, T) -> Bool) -> [T] {
let n = a.count
var z = [a, a] // the two working arrays
var d = 0 // z[d] is used for reading, z[1 - d] for writing
var width = 1
while width < n {
var i = 0
while i < n {
var j = i
var l = i
var r = i + width
let lmax = min(l + width, n)
let rmax = min(r + width, n)
while l < lmax && r < rmax {
if isOrderedBefore(z[d][l], z[d][r]) {
z[1 - d][j] = z[d][l]
l += 1
} else {
z[1 - d][j] = z[d][r]
r += 1
}
j += 1
}
while l < lmax {
z[1 - d][j] = z[d][l]
j += 1
l += 1
}
while r < rmax {
z[1 - d][j] = z[d][r]
j += 1
r += 1
}
i += width*2
}
width *= 2 // in each step, the subarray to merge becomes larger
d = 1 - d // swap active array
}
return z[d]
}