Data Structures Algorithms in Swift By Vincent Ngo Kelvin Lau The most popular and comprehensive book on Swift algorithms data structures Covers search, sort, trees, stacks, and more. Learn how to implement the most common and useful data structures and algorithms in Swift Understanding how data structures and algorithms work in code is crucial for creating efficient and scalable apps. Swift’s Standard Library has a small set of general purpose collection types, yet they definitely don’t cover every case In Data Structures and Algorithms in Swift, you’ll learn how to implement the most popular and useful data structures, and when and why you should use one particular datastructure or algorithm over another. This set of basic data structures and algorithms will serve as an excellent foundation for building more complex and specialpurpose constructs. As well, the highlevel expressiveness of Swift makes it an ideal choice for learning these core concepts without sacrificing performance. You’ll start with the fundamental structures of linked lists, queues and stacks, and see how to implement them in a highly Swiftlike way. Move on to working with various types of trees, including general purpose trees, binary trees, AVL trees, binary search trees, and tries. Go beyond bubble and insertion sort with betterperforming algorithms, including mergesort, radix sort, heap sort, and quicksort. Learn how to construct directed, nondirected and weighted graphs to represent many realworld models, and traverse graphs and trees efficiently with breadthfirst, depthfirst, Dijkstra’s and Prim’s algorithms to solve problems such as finding the shortest path or lowest cost in a network. By the end of this book, you’ll have handson experience solving common issues with data structures and algorithms — and you’ll be well on your way to developing your own efficient and useful implementations
Data Structures & Algorithms in Swift Data Structures & Algorithms in Swift Kelvin Lau & Vincent Ngo Copyright ©2019 Razeware LLC Notice of Rights All rights reserved No part of this book or corresponding materials (such as text, images, or source code) may be reproduced or distributed by any means without prior written permission of the copyright owner Notice of Liability This book and all corresponding materials (such as source code) are provided on an “as is” basis, without warranty of any kind, express of implied, including but not limited to the warranties of merchantability, fitness for a particular purpose, and noninfringement In no event shall the authors or copyright holders be liable for any claim, damages or other liability, whether in action of contract, tort or otherwise, arising from, out of or in connection with the software or the use of other dealing in the software Trademarks All trademarks and registered trademarks appearing in this book are the property of their own respective owners raywenderlich.com Data Structures & Algorithms in Swift Dedications "To my team, family, and friends." — Kelvin Lau "To my friends and family who I know will never read this book." — Vincent Ngo raywenderlich.com Data Structures & Algorithms in Swift About the Authors Kelvin Lau is an author of this book Kelvin is a physicist turned Swift iOS Developer While he’s currently entrenched with iOS development, he often reminisces of his aspirations to be part of the efforts in space exploration Outside of programming work, he’s an aspiring entrepreneur and musician You can find him on Twitter: @kelvinlauKL Vincent Ngo is an author of this book A software developer by day, and an iOS-Swift enthusiast by night, he believes that sharing knowledge is the best way to learn and grow as a developer Vincent starts every morning with a homemade green smoothie in hand to fuel his day When he is not in front of a computer, Vincent is training to play in small golf tournaments, doing headstands at various locations while on a hiking adventure, or looking up how to make tamago egg You can find him on Twitter: @vincentngo2 About the Editors Steven Van Impe is the technical editor of this book Steven is a computer science author and lecturer at the University College of Ghent, Belgium You can find Steven on Twitter as @pwsbooks Ray Fix is the final pass editor of this book A passionate Swift educator, enthusiast and advocate, he is actively using Swift to create Revolve: a next generation iPad controlled research microscope at Discover Echo Inc Ray is mostly-fluent in spoken and written Japanese and stays healthy by walking, jogging, and playing ultimate Frisbee When he is not doing one of those things, he is writing and dreaming of code in Swift Twitter: @rayfix raywenderlich.com Data Structures & Algorithms in Swift About the Contributors We’d also like to acknowledge the efforts of the following contributors to the Swift Algorithm Club GitHub repo (https://github.com/raywenderlich/swift-algorithmclub), upon whose work portions of this book are based Matthijs Hollemans, the original creator of the Swift Algorithm Club Matthijs contributed many of the implementations and corresponding explanations for the various data structures and algorithms in the Swift Algorithm Club that were used in this book, in particular: Graph, Heap, AVL Tree, BST, Breadth First Search, Depth First Search, Linked List, Stack & Queue, Tree, Selection Sort, Bubble Sort, Insertion Sort, Quick Sort, Merge Sort, and Heap Sort Matthijs spends much of his time now in machine learning Learn more at http://machinethink.net We’d also like to thank the following for their contributions to the repo: • • • • • • • Donald Pinckney, Graph https://github.com/donald-pinckney Christian Encarnacion, Trie and Radix Sort https://github.com/Thukor Kevin Randrup, Heap https://github.com/kevinrandrup Paulo Tanaka, Depth First Search https://github.com/paulot Nicolas Ameghino, BST https://github.com/nameghino Mike Taghavi, AVL Tree Chris Pilcher, Breadth First Search About the Artist Vicki Wenderlich is the designer and artist of the cover of this book She is Ray’s wife and business partner She is a digital artist who creates illustrations, game art and a lot of other art or design work for the tutorials and books on raywenderlich.com When she’s not making art, she loves hiking, a good glass of wine and attempting to create the perfect cheese plate raywenderlich.com Data Structures & Algorithms in Swift Table of Contents: Overview Book License 16 Who This Book Is For 17 What You Need 18 Book Source Code & Forums 19 About the Cover 21 Foreword 22 Section I: Introduction 24 Chapter 1: Why Learn Data Structures & Algorithms? 25 Chapter 2: Complexity 28 Chapter 3: Swift Standard Library 40 Section II: Elementary Data Structures 46 Chapter 4: Stack Data Structure 47 Chapter 5: Stack Challenges 53 Chapter 6: Linked List 56 Chapter 7: Linked List Challenges 79 Chapter 8: Queues 93 Chapter 9: Queue Challenges 114 Section III: Trees 128 Chapter 10: Trees 130 Chapter 11: Tree Challenges 140 raywenderlich.com Data Structures & Algorithms in Swift Chapter 12: Binary Trees 144 Chapter 13: Binary Tree Challenges 151 Chapter 14: Binary Search Trees 158 Chapter 15: Binary Search Tree Challenges 175 Chapter 16: AVL Trees 180 Chapter 17: AVL Tree Challenges 195 Chapter 18: Tries 200 Chapter 19: Trie Challenges 212 Chapter 20: Binary Search 215 Chapter 21: Binary Search Challenges 220 Chapter 22: The Heap Data Structure 226 Chapter 23: Heap Data Structure Challenges 243 Chapter 24: Priority Queue 249 Chapter 25: Priority Queue Challenges 254 Section IV: Sorting Algorithms 264 Chapter 26: O(n²) Sorting Algorithms 266 Chapter 27: O(n²) Sorting Challenges 277 Chapter 28: Merge Sort 281 Chapter 29: Merge Sort Challenges 288 Chapter 30: Radix Sort 293 Chapter 31: Radix Sort Challenges 299 Chapter 32: Heap Sort 304 Chapter 33: Heap Sort Challenges 310 raywenderlich.com Data Structures & Algorithms in Swift Chapter 34: Quicksort 315 Chapter 35: Quicksort Challenges 330 Section V: Graphs 336 Chapter 36: Graphs 338 Chapter 37: Graphs Challenges 360 Chapter 38: Breadth-First Search 364 Chapter 39: Breadth-First Search Challenges 371 Chapter 40: Depth-First Search 377 Chapter 41: Depth-First Search Challenges 385 Chapter 42: Dijkstra’s Algorithm 390 Chapter 43: Dijkstra’s Algorithm Challenges 406 Chapter 44: Prim’s Algorithm 409 Chapter 45: Prim’s Algorithm Challenges 421 Conclusion 430 raywenderlich.com Data Structures & Algorithms in Swift Table of Contents: Extended Book License 16 Who This Book Is For 17 What You Need 18 Book Source Code & Forums 19 About the Cover 21 Foreword 22 Section I: Introduction 24 Chapter 1: Why Learn Data Structures & Algorithms? 25 The goal of this book 27 Chapter 2: Complexity 28 Time complexity Space complexity Other notations Playground line-based execution bug Key points 29 36 38 38 39 Chapter 3: Swift Standard Library 40 Array Dictionary Set Key points 41 43 44 45 Section II: Elementary Data Structures 46 Chapter 4: Stack Data Structure 47 Stack operations 48 Implementation 48 push and pop operations 49 raywenderlich.com Data Structures & Algorithms in Swift Key points 52 Chapter 5: Stack Challenges 53 Solutions 54 Chapter 6: Linked List 56 Node LinkedList Adding values to the list Removing values from the list Swift collection protocols Becoming a Swift collection Value semantics and copy-on-write Optimizing COW Key points 57 58 59 63 68 69 71 74 78 Chapter 7: Linked List Challenges 79 Solutions 81 Chapter 8: Queues 93 Common operations 94 Example of a queue 94 Array-based implementation 95 Doubly linked list implementation 99 Ring buffer implementation 102 Double-stack implementation 107 Key points 112 Chapter 9: Queue Challenges 114 Solutions 117 Section III: Trees 128 Chapter 10: Trees 130 Terminology 131 Implementation 132 raywenderlich.com 10 Data Structures & Algorithms in Swift Chapter 44: Prim’s Algorithm Finding edges Besides copying the graph’s vertices, you also need to find and store the edges of every vertex you explore Open up Prim.swift and add the following to class Prim: internal func addAvailableEdges( for vertex: Vertex, in graph: Graph, check visited: Set, to priorityQueue: inout PriorityQueue) { for edge in graph.edges(from: vertex) { // if !visited.contains(edge.destination) { // priorityQueue.enqueue(edge) // } } } This method takes in four parameters: The current vertex The graph, wherein the current vertex is stored The vertices that have already been visited The priority queue to add all potential edges Within the function, you the following: Look at every edge adjacent to the current vertex Check to see if the destination vertex has already been visited If it has not been visited, you add the edge to the priority queue Now that we have established our helper methods, let’s implement Prim’s algorithm Producing a minimum spanning tree Add the following method to class Prim: public func produceMinimumSpanningTree(for graph: Graph) -> (cost: Double, mst: Graph) { // var cost = 0.0 // let mst = Graph() // var visited: Set = [] // var priorityQueue = PriorityQueue(sort: { // $0.weight ?? 0.0 < $1.weight ?? 0.0 }) raywenderlich.com 416 Data Structures & Algorithms in Swift } Chapter 44: Prim’s Algorithm // to be continued Here’s what you have so far: produceMinimumSpanningTree takes an undirected graph and returns a minimum spanning tree and its cost cost keeps track of the total weight of the edges in the minimum spanning tree This is a graph that will become your minimum spanning tree visited stores all vertices that have already been visited This is a min-priority queue to store edges Next, continue implementing produceMinimumSpanningTree with the following: mst.copyVertices(from: graph) // guard let start = graph.vertices.first else { // return (cost: cost, mst: mst) } visited.insert(start) // addAvailableEdges(for: start, // in: graph, check: visited, to: &priorityQueue) // to be continued This code initiates the algorithm: Copy all the vertices from the original graph to the minimum spanning tree Get the starting vertex from the graph Mark the starting vertex as visited Add all potential edges from the start vertex into the priority queue Finally, complete produceMinimumSpanningTree with: while let smallestEdge = priorityQueue.dequeue() { // let vertex = smallestEdge.destination // guard !visited.contains(vertex) else { // continue } raywenderlich.com 417 Data Structures & Algorithms in Swift Chapter 44: Prim’s Algorithm visited.insert(vertex) // cost += smallestEdge.weight ?? 0.0 // mst.add(.undirected, // from: smallestEdge.source, to: smallestEdge.destination, weight: smallestEdge.weight) } addAvailableEdges(for: vertex, // in: graph, check: visited, to: &priorityQueue) return (cost: cost, mst: mst) // Going over the code: Continue Prim’s algorithm until the queue of edges is empty Get the destination vertex If this vertex has been visited, restart the loop and get the next smallest edge Mark the destination vertex as visited Add the edge’s weight to the total cost Add the smallest edge into the minimum spanning tree you are constructing Add the available edges from the current vertex Once the priorityQueue is empty, return the minimum cost, and minimum spanning tree Testing your code raywenderlich.com 418 Data Structures & Algorithms in Swift Chapter 44: Prim’s Algorithm Navigate to the main playground, and you’ll see the graph above has been already constructed using an adjacency list It’s time to see Prim’s algorithm in action Add the following code: let (cost,mst) = Prim().produceMinimumSpanningTree(for: graph) print("cost: \(cost)") print("mst:") print(mst) This constructs a graph from the example section You’ll see the following output: cost: 15.0 mst: -> [ ] -> [ 3, ] -> [ 2, 1, ] -> [ ] -> [ 5, ] -> [ ] Performance In the algorithm above, you maintain three data structures: An adjacency list graph to build a minimum spanning tree Adding vertices and edges to an adjacency list is O(1) A Set to store all vertices you have visited Adding a vertex to the set and checking if the set contains a vertex also have a time complexity of O(1) A min-priority queue to store edges as you explore more vertices The priority queue is built on top of a heap and insertion takes O(log E) raywenderlich.com 419 Data Structures & Algorithms in Swift Chapter 44: Prim’s Algorithm The worst-case time complexity of Prim’s algorithm is O(E log E) This is because, each time you dequeue the smallest edge from the priority queue, you have to traverse all the edges of the destination vertex ( O(E) ) and insert the edge into the priority queue ( O(logE) ) Key points • A spanning tree is a subgraph of an undirected graph that contains all the vertices with the fewest number of edges • Prim’s algorithm is a greedy algorithm that constructs a minimum spanning tree • You can leverage three different data structures: Priority queue, set, and adjacency lists to construct Prim’s algorithm raywenderlich.com 420 45 Chapter 45: Prim’s Algorithm Challenges By Vincent Ngo Challenge 1: Minimum spanning tree of points Given a set of points, construct a minimum spanning tree connecting all of the points into a graph public func produceMinimumSpanningTree(with points: [CGPoint]) > (cost: Double, mst: Graph) { let graph = Graph() raywenderlich.com 421 Data Structures & Algorithms in Swift } Chapter 45: Prim’s Algorithm Challenges // Implement Solution return produceMinimumSpanningTree(for: graph) Challenge 2: What can you say about X? Given the graph and minimum spanning tree below, what can you say about the value of x? Challenge 3: Step-by-step Diagram Given the graph below, step through Prim’s algorithm to produce a minimum spanning tree, and provide the total cost Start at vertex B If two edges share the same weight, prioritize them alphabetically raywenderlich.com 422 Data Structures & Algorithms in Swift Chapter 45: Prim’s Algorithm Challenges Solutions Solution to Challenge You can think of the points as vertices on a graph To construct a minimum spanning tree with these points, you first need to know the weighted edge between every two points A Vertex requires its elements to be Hashable The starter project provides an extension to CGPoint: extension CGPoint: Hashable { public func hash(into hasher: inout Hasher) { hasher.combine(x) hasher.combine(y) } } Every vertex has an associated CGPoint To form an edge to another vertex (CGPoint), you need to calculate the distance between points: Add the following code below: extension CGPoint { func distanceSquared(to point: CGPoint) -> CGFloat { let xDistance = (x - point.x) let yDistance = (y - point.y) raywenderlich.com 423 Data Structures & Algorithms in Swift } } Chapter 45: Prim’s Algorithm Challenges return xDistance * xDistance + yDistance * yDistance func distance(to point: CGPoint) -> CGFloat { distanceSquared(to: point).squareRoot() } • distanceSquared(_:) calculates the hypotenuse’s squared value by adding the squared distance of the opposite and adjacent sides • distance(_:) returns the hypotenuse by taking the square root of the distance squared Now that you’ve established a way to calculate the distance between two points, you have all the information you need to form a minimum spanning tree! Recall: In the previous chapter you learned how to construct a minimum spanning tree You this by picking an arbitrary vertex and greedily pick the cheapest edge to one of its neighboring vertices till all the vertices are connected by an edge To leverage Prim’s algorithm you must form a complete graph with the given set of points A complete graph is a undirected graph where all pair of vertices are connected by a unique edge Imagine a sided pentagon with vertices Each vertex is connected to every other vertex to form a star! raywenderlich.com 424 Data Structures & Algorithms in Swift Chapter 45: Prim’s Algorithm Challenges Add the following code: extension Prim where T == CGPoint { public func createCompleteGraph(with points: [CGPoint]) -> Graph { let completeGraph = Graph() // points.forEach { point in // completeGraph.createVertex(data: point) } // completeGraph.vertices.forEach { currentVertex in completeGraph.vertices.forEach { vertex in if currentVertex != vertex { let distance = Double(currentVertex.data.distance(to: vertex.data)) // completeGraph.addDirectedEdge(from: currentVertex, to: vertex, weight: distance) // } } } } } return completeGraph // Here you create an extension as part of Prim, and check to see if the element is of type CGPoint Create an empty new graph Go through each point and create a vertex Loop through each vertex, and for every other vertex as long as the vertex is not the same Calculate the distance between the two vertices Add a directed edge between the two vertices Return the complete graph raywenderlich.com 425 Data Structures & Algorithms in Swift Chapter 45: Prim’s Algorithm Challenges You can now form a complete graph using the given points, and leverage prim’s algorithm to form a minimum spanning tree Add the following after createCompleteGraph(_:): public func produceMinimumSpanningTree(with points: [CGPoint]) > (cost: Double, mst: Graph) { let completeGraph = createCompleteGraph(with: points) return produceMinimumSpanningTree(for: completeGraph) } Below shows a sample data set showing how the minimum spanning tree is formed: raywenderlich.com 426 Data Structures & Algorithms in Swift Chapter 45: Prim’s Algorithm Challenges Solution to Challenge The value of x is less than or equal to raywenderlich.com 427 Data Structures & Algorithms in Swift Chapter 45: Prim’s Algorithm Challenges Solution to Challenge Edges [A:2, D:8, C:6, E:2] Edges part of MST: [A:2] Explored [A, B] Edges [D:8, C:6, E:2, D:3, C:21] Edges part of MST: [A:2, E:2] Explored [A, B, E] raywenderlich.com 428 Data Structures & Algorithms in Swift Chapter 45: Prim’s Algorithm Challenges Edges [D:8, C:6, D:3, C:21, D:12, C:4] Edges part of MST: [A:2, E:2, D:3] Explored [A, B, E, D] Edges [C:6, C:21, C:4] Edges part of MST: [A:2, E:2, D:3, C:4] Explored [A, B, E, D, C] Edges [A:2, E:2, D:3, C:4] Explored [A, B, E, D, C] Total Cost: 11 raywenderlich.com 429 C Conclusion We hope you learned a lot about data structures and algorithms in Swift as you read this book — and had some fun in the process! Knowing when and why to apply data structures and algorithms goes beyond just acing that whiteboard interview With the knowledge you’ve gained here, you can easily and efficiently solve pretty much any data manipulation or graph analysis issue put in front of you If you have any questions or comments as you work through this book, please stop by our forums at http://forums.raywenderlich.com and look for the particular forum category for this book Thank you again for purchasing this book Your continued support is what makes the tutorials, books, videos, conferences and other things we at raywenderlich.com possible, and we truly appreciate it! Wishing you all the best in your continued algorithmic adventures, – Kelvin, Vincent, `Ray`, Steven, Chris and Manda The Data Structures & Algorithms in Swift team raywenderlich.com 430 .. .Data Structures & Algorithms in Swift Data Structures & Algorithms in Swift Kelvin Lau & Vincent Ngo Copyright ©2019 Razeware LLC Notice of Rights... source code included in Data Structures & Algorithms in Swift is for your personal use only You are NOT allowed to distribute or sell the source code in Data Structures & Algorithms in Swift without... building blocks for useful programs So why should you learn data structures and algorithms? raywenderlich. com 25 Data Structures & Algorithms in Swift Chapter 1: Why Learn Data Structures & Algorithms?