Skip to content

Commit 4b2fc0b

Browse files
committed
Refactored Heap data structure into separate playground source file
1 parent d4330f3 commit 4b2fc0b

File tree

2 files changed

+250
-248
lines changed

2 files changed

+250
-248
lines changed

Data Structures.playground/Pages/Heap.xcplaygroundpage/Contents.swift

+1-248
Original file line numberDiff line numberDiff line change
@@ -2,254 +2,6 @@
22

33
import Foundation
44

5-
public struct Heap<T> where T: Comparable {
6-
7-
// MARK: - Constants
8-
9-
private let zero = 0
10-
11-
// MARK: - Properties
12-
13-
var nodes = [T]()
14-
15-
public var isEmpty: Bool {
16-
return nodes.isEmpty
17-
}
18-
19-
public var count: Int {
20-
return nodes.count
21-
}
22-
23-
// MARK: - Private properties
24-
25-
fileprivate var order: (T, T) -> Bool
26-
27-
// MARK: - Initializers
28-
29-
public init(order: @escaping (T, T) -> Bool) {
30-
self.order = order
31-
}
32-
33-
public init(array: [T], order: @escaping (T, T) -> Bool) {
34-
self.order = order
35-
setup(using: array)
36-
}
37-
38-
// MARK: - Methods
39-
40-
public func peek() -> T? {
41-
return nodes.first
42-
}
43-
44-
public mutating func insert(node: T) {
45-
nodes.append(node)
46-
shiftUp(count - 1)
47-
}
48-
49-
public mutating func insert<S: Sequence>(_ sequence: S) where S.Iterator.Element == T {
50-
sequence.forEach { element in
51-
insert(node: element)
52-
}
53-
}
54-
55-
public mutating func replace(elementAt index: Int, with value: T) {
56-
guard index < nodes.count else { return }
57-
remove(at: index)
58-
insert(node: value)
59-
}
60-
61-
@discardableResult public mutating func remove() -> T? {
62-
guard !nodes.isEmpty else { return nil }
63-
64-
switch nodes.count {
65-
case 1:
66-
return nodes.removeLast()
67-
default:
68-
let value = nodes[zero]
69-
nodes[zero] = nodes.removeLast()
70-
shiftDown(zero)
71-
return value
72-
}
73-
}
74-
75-
@discardableResult public mutating func remove(at index: Int) -> T? {
76-
guard index < nodes.count else { return nil }
77-
let size = count - 1
78-
79-
if index != size {
80-
nodes.swapAt(index, size)
81-
shiftDown(from: index, until: size)
82-
shiftUp(index)
83-
}
84-
return nodes.removeLast()
85-
}
86-
87-
// MARK: - Shifting
88-
89-
internal mutating func shiftDown(from startIndex: Int, until endIndex: Int) {
90-
let leftChildIndex = self.leftChildIndex(of: startIndex)
91-
let rightChildIndex = leftChildIndex + 1
92-
var first = startIndex
93-
94-
if leftChildIndex < endIndex && order(nodes[leftChildIndex], nodes[first]) {
95-
first = leftChildIndex
96-
}
97-
if rightChildIndex < endIndex && order(nodes[rightChildIndex], nodes[first]) {
98-
first = rightChildIndex
99-
}
100-
if first == startIndex { return }
101-
102-
nodes.swapAt(startIndex, first)
103-
shiftDown(from: first, until: endIndex)
104-
}
105-
106-
internal mutating func shiftDown(_ index: Int) {
107-
shiftDown(from: index, until: count)
108-
}
109-
110-
internal mutating func shiftUp(_ index: Int) {
111-
var childIndex = index
112-
let child = nodes[childIndex]
113-
var parentIndex = self.parentIndex(of: childIndex)
114-
115-
while childIndex > 0 && order(child, nodes[parentIndex]) {
116-
nodes[childIndex] = nodes[parentIndex]
117-
childIndex = parentIndex
118-
parentIndex = self.parentIndex(of: childIndex)
119-
}
120-
nodes[childIndex] = child
121-
}
122-
123-
// MARK: - Child indicies
124-
125-
@inline(__always) internal func rightChildIndex(of index: Int) -> Int {
126-
return 2 * index + 2
127-
}
128-
129-
@inline(__always) internal func leftChildIndex(of index: Int) -> Int {
130-
return 2 * index + 1
131-
}
132-
133-
@inline(__always) internal func parentIndex(of index: Int) -> Int {
134-
return (index - 1) / 2
135-
}
136-
137-
// MARK: - Private helpers
138-
139-
private mutating func setup(using nodesArray: [T]) {
140-
self.nodes = nodesArray
141-
let strideCondition = stride(from: (count / 2 - 1), through: 0, by: -1)
142-
143-
for index in strideCondition {
144-
shiftDown(index)
145-
}
146-
}
147-
148-
}
149-
150-
/*:
151-
Basically we created wrapper around standard Array type. However we need to carefully take a look at the two important pieces of the implenentation:
152-
- T type which is a generic placeholder
153-
- Custom initializer that accepts iterator element of Sequence protocol - this is simply a complementary initializer that allows client-developer to pass other Stack structs, Arra or any other type that conforms to Sequnce protocol to use it at initialization phase. Convenience and compatability with standard library - nothing sophisticated.
154-
155-
156-
The following extension adds support for debugging capabilites for both regular "print" and "debugPrint" statements
157-
*/
158-
extension Heap: CustomStringConvertible, CustomDebugStringConvertible {
159-
160-
// MARK: - Properties
161-
162-
public var description: String {
163-
return nodes.description
164-
}
165-
166-
public var debugDescription: String {
167-
return prettyPrint()
168-
}
169-
170-
// MARK: - Helpers
171-
172-
private func prettyPrint() -> String {
173-
var out = String()
174-
var iterator = 2
175-
176-
var copy = Array(nodes)
177-
var counter = copy.count
178-
179-
while counter > 0, copy.count > 0 {
180-
if counter == nodes.count {
181-
// remove the first element only
182-
out += "{ \(copy.removeFirst()) "
183-
counter -= 1
184-
185-
} else {
186-
out += "{ "
187-
var temp = iterator
188-
189-
while temp > 0, copy.count > 0 {
190-
out += "\(copy.removeFirst()) "
191-
temp -= 1
192-
}
193-
194-
counter -= iterator
195-
iterator = iterator * 2
196-
}
197-
out += "} \n"
198-
}
199-
200-
return out
201-
}
202-
203-
}
204-
205-
extension Heap: ExpressibleByArrayLiteral {
206-
207-
public init(arrayLiteral elements: T...) {
208-
self.init(array: elements, order: >)
209-
}
210-
}
211-
212-
/*:
213-
Adds support for Sequence protocol. The Swift's runtime will call the makeIterator() method to initialize the for...in loop. All we need to do is to return some soft of iterator instance that conforms to IteratorProtocol. Iterator protocol allows us to return an iterator based on the type of elements out target type contains - in this particular case it is Stack.
214-
*/
215-
extension Heap: Sequence{
216-
217-
public func makeIterator() -> AnyIterator<T> {
218-
let idexedIterator = IndexingIterator(_elements: self.nodes.lazy.reversed())
219-
return AnyIterator(idexedIterator)
220-
}
221-
}
222-
223-
224-
225-
// MARK: - Heap Sort. Should be decomposed into separete file for sorting algorithms.
226-
// - Note that it is very similar to Selection sort approach.
227-
// - Complexity of the sorting algorithms is O(n log n) in best, worst and average cases. The heap is traversed once (which is a list and is O(n)) and then every time the elements are swapped, a shift down operation is performed which is O(log n). As a result we have O(n log n) coomplexity.
228-
extension Heap {
229-
func sorted() -> [T] {
230-
var heap = Heap(array: nodes, order: order)
231-
232-
for index in self.nodes.indices.reversed() {
233-
heap.nodes.swapAt(0, index)
234-
heap.shiftDown(from: 0, until: index)
235-
}
236-
return heap.nodes
237-
}
238-
}
239-
240-
241-
// MARK: - Searching
242-
extension Heap where T: Equatable {
243-
244-
/// Gets the index of a node from the Heap
245-
///
246-
/// - Parameter node: is a Node of type T that is going to be searched
247-
/// - Returns: is an index in Heap array
248-
public func index(of node: T) -> Int? {
249-
return nodes.index(where: { $0 == node })
250-
}
251-
}
252-
2535
//: Usage
2546

2557
var maxHeap = Heap<Int>(order: >)
@@ -269,4 +21,5 @@ heap.index(of: 3)
26921
let sortedHeap = heap.sorted()
27022
debugPrint(sortedHeap)
27123

24+
27225
//: [Next](@next)

0 commit comments

Comments
 (0)