forked from kodecocodes/swift-algorithm-club
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Heap.swift
executable file
·223 lines (190 loc) · 6.47 KB
/
Heap.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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
//
// Heap.swift
// Written for the Swift Algorithm Club by Kevin Randrup and Matthijs Hollemans
//
public struct Heap<T> {
/** The array that stores the heap's nodes. */
var nodes = [T]()
/**
* Determines how to compare two nodes in the heap.
* Use '>' for a max-heap or '<' for a min-heap,
* or provide a comparing method if the heap is made
* of custom elements, for example tuples.
*/
private var orderCriteria: (T, T) -> Bool
/**
* Creates an empty heap.
* The sort function determines whether this is a min-heap or max-heap.
* For comparable data types, > makes a max-heap, < makes a min-heap.
*/
public init(sort: @escaping (T, T) -> Bool) {
self.orderCriteria = sort
}
/**
* Creates a heap from an array. The order of the array does not matter;
* the elements are inserted into the heap in the order determined by the
* sort function. For comparable data types, '>' makes a max-heap,
* '<' makes a min-heap.
*/
public init(array: [T], sort: @escaping (T, T) -> Bool) {
self.orderCriteria = sort
configureHeap(from: array)
}
/**
* Configures the max-heap or min-heap from an array, in a bottom-up manner.
* Performance: This runs pretty much in O(n).
*/
private mutating func configureHeap(from array: [T]) {
nodes = array
for i in stride(from: (nodes.count/2-1), through: 0, by: -1) {
shiftDown(i)
}
}
public var isEmpty: Bool {
return nodes.isEmpty
}
public var count: Int {
return nodes.count
}
/**
* Returns the index of the parent of the element at index i.
* The element at index 0 is the root of the tree and has no parent.
*/
@inline(__always) internal func parentIndex(ofIndex i: Int) -> Int {
return (i - 1) / 2
}
/**
* Returns the index of the left child of the element at index i.
* Note that this index can be greater than the heap size, in which case
* there is no left child.
*/
@inline(__always) internal func leftChildIndex(ofIndex i: Int) -> Int {
return 2*i + 1
}
/**
* Returns the index of the right child of the element at index i.
* Note that this index can be greater than the heap size, in which case
* there is no right child.
*/
@inline(__always) internal func rightChildIndex(ofIndex i: Int) -> Int {
return 2*i + 2
}
/**
* Returns the maximum value in the heap (for a max-heap) or the minimum
* value (for a min-heap).
*/
public func peek() -> T? {
return nodes.first
}
/**
* Adds a new value to the heap. This reorders the heap so that the max-heap
* or min-heap property still holds. Performance: O(log n).
*/
public mutating func insert(_ value: T) {
nodes.append(value)
shiftUp(nodes.count - 1)
}
/**
* Adds a sequence of values to the heap. This reorders the heap so that
* the max-heap or min-heap property still holds. Performance: O(log n).
*/
public mutating func insert<S: Sequence>(_ sequence: S) where S.Iterator.Element == T {
for value in sequence {
insert(value)
}
}
/**
* Allows you to change an element. This reorders the heap so that
* the max-heap or min-heap property still holds.
*/
public mutating func replace(index i: Int, value: T) {
guard i < nodes.count else { return }
remove(at: i)
insert(value)
}
/**
* Removes the root node from the heap. For a max-heap, this is the maximum
* value; for a min-heap it is the minimum value. Performance: O(log n).
*/
@discardableResult public mutating func remove() -> T? {
guard !nodes.isEmpty else { return nil }
if nodes.count == 1 {
return nodes.removeLast()
} else {
// Use the last node to replace the first one, then fix the heap by
// shifting this new first node into its proper position.
let value = nodes[0]
nodes[0] = nodes.removeLast()
shiftDown(0)
return value
}
}
/**
* Removes an arbitrary node from the heap. Performance: O(log n).
* Note that you need to know the node's index.
*/
@discardableResult public mutating func remove(at index: Int) -> T? {
guard index < nodes.count else { return nil }
let size = nodes.count - 1
if index != size {
nodes.swapAt(index, size)
shiftDown(from: index, until: size)
shiftUp(index)
}
return nodes.removeLast()
}
/**
* Takes a child node and looks at its parents; if a parent is not larger
* (max-heap) or not smaller (min-heap) than the child, we exchange them.
*/
internal mutating func shiftUp(_ index: Int) {
var childIndex = index
let child = nodes[childIndex]
var parentIndex = self.parentIndex(ofIndex: childIndex)
while childIndex > 0 && orderCriteria(child, nodes[parentIndex]) {
nodes[childIndex] = nodes[parentIndex]
childIndex = parentIndex
parentIndex = self.parentIndex(ofIndex: childIndex)
}
nodes[childIndex] = child
}
/**
* Looks at a parent node and makes sure it is still larger (max-heap) or
* smaller (min-heap) than its childeren.
*/
internal mutating func shiftDown(from index: Int, until endIndex: Int) {
let leftChildIndex = self.leftChildIndex(ofIndex: index)
let rightChildIndex = leftChildIndex + 1
// Figure out which comes first if we order them by the sort function:
// the parent, the left child, or the right child. If the parent comes
// first, we're done. If not, that element is out-of-place and we make
// it "float down" the tree until the heap property is restored.
var first = index
if leftChildIndex < endIndex && orderCriteria(nodes[leftChildIndex], nodes[first]) {
first = leftChildIndex
}
if rightChildIndex < endIndex && orderCriteria(nodes[rightChildIndex], nodes[first]) {
first = rightChildIndex
}
if first == index { return }
nodes.swapAt(index, first)
shiftDown(from: first, until: endIndex)
}
internal mutating func shiftDown(_ index: Int) {
shiftDown(from: index, until: nodes.count)
}
}
// MARK: - Searching
extension Heap where T: Equatable {
/** Get the index of a node in the heap. Performance: O(n). */
public func index(of node: T) -> Int? {
return nodes.index(where: { $0 == node })
}
/** Removes the first occurrence of a node from the heap. Performance: O(n log n). */
@discardableResult public mutating func remove(node: T) -> T? {
if let index = index(of: node) {
return remove(at: index)
}
return nil
}
}