├── _config.yml ├── cover-algo-datastruct.png ├── Data Structures.playground ├── Resources │ ├── bear.png │ ├── chick.png │ ├── cow.png │ ├── dog.png │ ├── duck.png │ ├── frog.png │ ├── goat.png │ ├── hippo.png │ ├── horse.png │ ├── owl.png │ ├── panda.png │ ├── pig.png │ ├── rhino.png │ ├── sloth.png │ ├── snake.png │ ├── whale.png │ ├── zebra.png │ ├── buffalo.png │ ├── chicken.png │ ├── giraffe.png │ ├── gorilla.png │ ├── monkey.png │ ├── narwhal.png │ ├── parrot.png │ ├── penguin.png │ ├── rabbit.png │ ├── walrus.png │ ├── book spine.png │ ├── crocodile.png │ └── elephant.png ├── Pages │ ├── PriorityQueue.xcplaygroundpage │ │ └── Contents.swift │ ├── Heap.xcplaygroundpage │ │ └── Contents.swift │ ├── Main.xcplaygroundpage │ │ └── Contents.swift │ ├── Tree.xcplaygroundpage │ │ └── Contents.swift │ ├── Dequeue.xcplaygroundpage │ │ └── Contents.swift │ ├── Linked List.xcplaygroundpage │ │ └── Contents.swift │ ├── CircularBuffer.xcplaygroundpage │ │ └── Contents.swift │ ├── BinaryTree.xcplaygroundpage │ │ └── Contents.swift │ ├── DoublyLinkedList.xcplaygroundpage │ │ └── Contents.swift │ ├── Graph.xcplaygroundpage │ │ └── Contents.swift │ ├── Red-Black Tree.xcplaygroundpage │ │ └── Content.swift │ ├── Queue.xcplaygroundpage │ │ └── Contents.swift │ ├── Stack.xcplaygroundpage │ │ └── Contents.swift │ └── BinarySearchTree.xcplaygroundpage │ │ └── Contents.swift ├── contents.xcplayground └── Sources │ ├── StackElementNode.swift │ ├── QueueElementNode.swift │ ├── PriorityQueue.swift │ └── Heap.swift ├── String Search.playground ├── contents.xcplayground └── Contents.swift ├── Search.playground ├── contents.xcplayground └── Pages │ ├── Linear.xcplaygroundpage │ └── Contents.swift │ └── Binary.xcplaygroundpage │ └── Contents.swift ├── Sort.playground ├── contents.xcplayground └── Pages │ ├── Bubble.xcplaygroundpage │ └── Contents.swift │ ├── Insertion.xcplaygroundpage │ └── Contents.swift │ ├── Shell.xcplaygroundpage │ └── Contents.swift │ ├── Radix.xcplaygroundpage │ └── Contents.swift │ ├── Quick-Lomuto-Scheme.xcplaygroundpage │ └── Contents.swift │ ├── Quick-Hoare-Scheme.xcplaygroundpage │ └── Contents.swift │ └── Merge.xcplaygroundpage │ └── Contents.swift ├── LICENSE ├── .gitignore ├── CODE_OF_CONDUCT.md └── README.md /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-architect -------------------------------------------------------------------------------- /cover-algo-datastruct.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/cover-algo-datastruct.png -------------------------------------------------------------------------------- /Data Structures.playground/Resources/bear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/Data Structures.playground/Resources/bear.png -------------------------------------------------------------------------------- /Data Structures.playground/Resources/chick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/Data Structures.playground/Resources/chick.png -------------------------------------------------------------------------------- /Data Structures.playground/Resources/cow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/Data Structures.playground/Resources/cow.png -------------------------------------------------------------------------------- /Data Structures.playground/Resources/dog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/Data Structures.playground/Resources/dog.png -------------------------------------------------------------------------------- /Data Structures.playground/Resources/duck.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/Data Structures.playground/Resources/duck.png -------------------------------------------------------------------------------- /Data Structures.playground/Resources/frog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/Data Structures.playground/Resources/frog.png -------------------------------------------------------------------------------- /Data Structures.playground/Resources/goat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/Data Structures.playground/Resources/goat.png -------------------------------------------------------------------------------- /Data Structures.playground/Resources/hippo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/Data Structures.playground/Resources/hippo.png -------------------------------------------------------------------------------- /Data Structures.playground/Resources/horse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/Data Structures.playground/Resources/horse.png -------------------------------------------------------------------------------- /Data Structures.playground/Resources/owl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/Data Structures.playground/Resources/owl.png -------------------------------------------------------------------------------- /Data Structures.playground/Resources/panda.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/Data Structures.playground/Resources/panda.png -------------------------------------------------------------------------------- /Data Structures.playground/Resources/pig.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/Data Structures.playground/Resources/pig.png -------------------------------------------------------------------------------- /Data Structures.playground/Resources/rhino.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/Data Structures.playground/Resources/rhino.png -------------------------------------------------------------------------------- /Data Structures.playground/Resources/sloth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/Data Structures.playground/Resources/sloth.png -------------------------------------------------------------------------------- /Data Structures.playground/Resources/snake.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/Data Structures.playground/Resources/snake.png -------------------------------------------------------------------------------- /Data Structures.playground/Resources/whale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/Data Structures.playground/Resources/whale.png -------------------------------------------------------------------------------- /Data Structures.playground/Resources/zebra.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/Data Structures.playground/Resources/zebra.png -------------------------------------------------------------------------------- /Data Structures.playground/Resources/buffalo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/Data Structures.playground/Resources/buffalo.png -------------------------------------------------------------------------------- /Data Structures.playground/Resources/chicken.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/Data Structures.playground/Resources/chicken.png -------------------------------------------------------------------------------- /Data Structures.playground/Resources/giraffe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/Data Structures.playground/Resources/giraffe.png -------------------------------------------------------------------------------- /Data Structures.playground/Resources/gorilla.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/Data Structures.playground/Resources/gorilla.png -------------------------------------------------------------------------------- /Data Structures.playground/Resources/monkey.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/Data Structures.playground/Resources/monkey.png -------------------------------------------------------------------------------- /Data Structures.playground/Resources/narwhal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/Data Structures.playground/Resources/narwhal.png -------------------------------------------------------------------------------- /Data Structures.playground/Resources/parrot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/Data Structures.playground/Resources/parrot.png -------------------------------------------------------------------------------- /Data Structures.playground/Resources/penguin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/Data Structures.playground/Resources/penguin.png -------------------------------------------------------------------------------- /Data Structures.playground/Resources/rabbit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/Data Structures.playground/Resources/rabbit.png -------------------------------------------------------------------------------- /Data Structures.playground/Resources/walrus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/Data Structures.playground/Resources/walrus.png -------------------------------------------------------------------------------- /Data Structures.playground/Resources/book spine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/Data Structures.playground/Resources/book spine.png -------------------------------------------------------------------------------- /Data Structures.playground/Resources/crocodile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/Data Structures.playground/Resources/crocodile.png -------------------------------------------------------------------------------- /Data Structures.playground/Resources/elephant.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eleev/swift-algorithms-data-structs/HEAD/Data Structures.playground/Resources/elephant.png -------------------------------------------------------------------------------- /String Search.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Search.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Data Structures.playground/Pages/PriorityQueue.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: [Previous](@previous) 2 | 3 | import Foundation 4 | 5 | //: Usage: 6 | 7 | var queue = PriorityQueue(elements: [2, 1, 4, 3, 5], order: >) 8 | queue.enqueue(9) 9 | 10 | print(#function + " queue: " , queue) 11 | 12 | //: [Next](@next) 13 | -------------------------------------------------------------------------------- /Sort.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Data Structures.playground/Pages/Heap.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: [Previous](@previous) 2 | 3 | import Foundation 4 | 5 | //: Usage 6 | 7 | var maxHeap = Heap(order: >) 8 | maxHeap.insert(node: 1) 9 | maxHeap.insert(node: 5) 10 | maxHeap.insert(node: 2) 11 | maxHeap.insert(node: 7) 12 | maxHeap.insert(node: 9) 13 | 14 | debugPrint(maxHeap) 15 | 16 | var heap: Heap = [3, 1, 4, 2, 7, 9, 8] 17 | debugPrint(heap) 18 | 19 | heap.index(of: 3) 20 | 21 | let sortedHeap = heap.sorted() 22 | debugPrint(sortedHeap) 23 | 24 | 25 | //: [Next](@next) 26 | -------------------------------------------------------------------------------- /Search.playground/Pages/Linear.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: [Previous](@previous) 2 | 3 | import Foundation 4 | 5 | extension Array where Element: Comparable { 6 | 7 | func linearSearch(key: Element) -> Int? { 8 | for (index, instance) in self.enumerated() where instance == key { 9 | return index 10 | } 11 | return nil 12 | } 13 | 14 | } 15 | 16 | //: Usage 17 | 18 | let strings = ["Steve", "Apple", "bar", "foo", "juce", "iPad", "macOS", "book", "universal"] 19 | var index = strings.linearSearch(key: "foo") 20 | 21 | let numbers = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67] 22 | index = numbers.linearSearch(key: 67) 23 | 24 | //: [Next](@next) 25 | -------------------------------------------------------------------------------- /Data Structures.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /Data Structures.playground/Sources/StackElementNode.swift: -------------------------------------------------------------------------------- 1 | import SpriteKit 2 | 3 | public class StackElementNode: SKSpriteNode { 4 | 5 | public init() { 6 | let size = CGSize(width: 50, height: 150) 7 | 8 | let randomHue = CGFloat(arc4random_uniform(255)) 9 | let randomSaturation = CGFloat(arc4random_uniform(255)) 10 | let randomBrightness = CGFloat(arc4random_uniform(255)) 11 | let color = SKColor(hue: randomHue, saturation: randomSaturation, brightness: randomBrightness, alpha: 1.0) 12 | 13 | super.init(texture: SKTexture(imageNamed: "book spine"), color: color, size: size) 14 | zRotation = 90 * CGFloat.pi / 180.0 15 | } 16 | 17 | public required init?(coder aDecoder: NSCoder) { 18 | fatalError(#function + " required initizlier for NSCode has not been implemented") 19 | } 20 | } 21 | 22 | -------------------------------------------------------------------------------- /Sort.playground/Pages/Bubble.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: [Previous](@previous) 2 | 3 | import Foundation 4 | 5 | extension Array where Element: Comparable { 6 | 7 | mutating func bubbleSorted(order sign: (Element, Element) -> Bool) { 8 | let count = self.count 9 | 10 | for _ in 0...count { 11 | for value in 1...count - 2 { 12 | if sign(self[value - 1], self[value]) { 13 | // self.swapAt(value - 1, value) 14 | let largerValue = self[value - 1] 15 | self[value - 1] = self[value] 16 | self[value] = largerValue 17 | } 18 | } 19 | } 20 | } 21 | 22 | } 23 | 24 | //: Usage 25 | 26 | var nums = [1, 5, 6, 3, 2, 7, 8, 5, 8, 4, 2, 9, 0] 27 | nums.bubbleSorted(order: >) 28 | 29 | debugPrint(#function + " sorted: ", nums) 30 | 31 | //: [Next](@next) 32 | -------------------------------------------------------------------------------- /Data Structures.playground/Pages/Main.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | /*: 2 | This is a Playground Book that contains examples of common Algorithms and Data Structures implemented using the latest Swift programming language version. The development attempts to fully utilize standard library and protocol-oriented paradigm. The book provides visualizations using SpriteKit and SceneKit frameworks. 📖 3 | 4 | Here you can find the following list of data structures: 5 | - [Stack](Stack) 6 | - [Circular Buffer](CircularBuffer) 7 | - [Queue](Queue) 8 | - [Priority Queue](PriorityQueue) 9 | - [Linked List](LinkedList) 10 | - Doubly Linked List 11 | - Circular Linked List 12 | - [Tree](Tree) 13 | - [Binary Tree](BinaryTree) 14 | - Binary Search Tree 15 | - B-Tree 16 | - Splay Tree 17 | - Red-Black Tree 18 | - AVL Tree 19 | - Trie Tree 20 | - Radix Tree 21 | - Quad Tree 22 | - Octree 23 | - [Heap](Heap) 24 | - [Graph](Graph) 25 | */ 26 | 27 | //: [Next](@next) 28 | -------------------------------------------------------------------------------- /Sort.playground/Pages/Insertion.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: Playground - noun: a place where people can play 2 | 3 | import UIKit 4 | import Foundation 5 | 6 | // MARK: - Implementation 7 | 8 | extension Array where Element: Comparable { 9 | 10 | public mutating func insertionSort(order: (Element, Element)->Bool) -> Array { 11 | if self.count <= 1 { 12 | return self 13 | } 14 | for i in 1.. 0 && order(self[j - 1], temp) { 19 | self[j] = self[j - 1] 20 | j -= 1 21 | } 22 | self[j] = temp 23 | } 24 | 25 | return self 26 | } 27 | 28 | } 29 | 30 | // MARK: - Usage 31 | 32 | var intData = [1,3,4,-10,34,234] 33 | intData.insertionSort(order: <) 34 | 35 | var stringData = ["go","ado","hello","we","can do"] 36 | stringData.insertionSort(order: >) 37 | -------------------------------------------------------------------------------- /Data Structures.playground/Sources/QueueElementNode.swift: -------------------------------------------------------------------------------- 1 | import SpriteKit 2 | 3 | public class QueueElementNode: SKSpriteNode { 4 | 5 | static let animals = [ 6 | "bear", "buffalo", "chick", "cow", "crocodile", "dog", "duck", "elephant", "frog", "giraffe", "goat", "gorilla", "hippo", "horse", "monkey", "narwhal", "owl", "panda", "parrot", "penguin", "pig", "rabbit", "rhino", "sloth", "snake", "walrus", "whale", "zebra" 7 | ] 8 | 9 | public init() { 10 | let cap = QueueElementNode.animals.count - 1 11 | let randomAnimalIndex = Int(arc4random_uniform(UInt32(cap))) 12 | let textureName = QueueElementNode.animals[randomAnimalIndex] 13 | let texture = SKTexture(imageNamed: textureName) 14 | super.init(texture: texture, color: .clear, size: texture.size()) 15 | xScale = 0.5 16 | yScale = 0.5 17 | } 18 | 19 | public required init?(coder aDecoder: NSCoder) { 20 | fatalError(#function + " required initizlier for NSCode has not been implemented") 21 | } 22 | } 23 | 24 | 25 | -------------------------------------------------------------------------------- /Sort.playground/Pages/Shell.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: [Previous](@previous) 2 | 3 | import Foundation 4 | 5 | // Shell sort is an improved version of insertion sort. The original list is broken into smaller sublists and then individually sorted using insertion sort. 6 | 7 | extension Array where Element: Comparable { 8 | 9 | public mutating func shellSorted(order sign: (Element, Element) -> Bool) { 10 | var subcount = self.count / 2 11 | 12 | while subcount > 0 { 13 | for i in 0..= subcount && sign(self[j - subcount], temp) { 18 | self[j] = self[j - subcount] 19 | j -= subcount 20 | } 21 | self[j] = temp 22 | } 23 | subcount /= 2 24 | } 25 | 26 | } 27 | } 28 | 29 | //: Usage 30 | 31 | var data = [4, 1, 5, 6, 1, 2, 9, 8, 5, 4, 2, 7, 6] 32 | data.shellSorted(order: >) 33 | 34 | //: [Next](@next) 35 | -------------------------------------------------------------------------------- /Sort.playground/Pages/Radix.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: [Previous](@previous) 2 | 3 | import Foundation 4 | 5 | // MARK: - Radix sort relies on the positional notation of integers and sorts an integer array for linear time. 6 | extension Array where Element == Int { 7 | 8 | public mutating func radixSort(of base: Int = 10) { 9 | var isDone = false 10 | var digits = 1 11 | 12 | while !isDone { 13 | isDone = true 14 | var buckets: [[Int]] = .init(repeating: [], count: base) 15 | 16 | forEach { number in 17 | let remainig = number / digits 18 | let digit = remainig % base 19 | buckets[digit].append(number) 20 | 21 | if remainig > 0 { 22 | isDone = false 23 | } 24 | } 25 | digits *= base 26 | self = buckets.flatMap { $0 } 27 | } 28 | } 29 | } 30 | 31 | 32 | //: Usage: 33 | 34 | var numbers = [88, 410, 1, 1772, 20, 6, 4, 45688] 35 | numbers.radixSort() 36 | 37 | //: [Next](@next) 38 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Astemir Eleev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Sort.playground/Pages/Quick-Lomuto-Scheme.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: [Previous](@previous) 2 | 3 | import Foundation 4 | 5 | extension Array where Element: Comparable { 6 | 7 | static func quickSort(array: inout [Element], lowest: Int, highest: Int) { 8 | if lowest < highest { 9 | let pivot = Array.partitionLomuto(array: &array, lowest: lowest, highest: highest) 10 | 11 | Array.quickSort(array: &array, lowest: lowest, highest: pivot - 1) 12 | Array.quickSort(array: &array, lowest: pivot + 1, highest: highest) 13 | } else { 14 | debugPrint(#function + " lowest param is bigger than highest: ", lowest, highest) 15 | } 16 | } 17 | 18 | private static func partitionLomuto(array: inout [Element], lowest: Int, highest: Int) -> Int { 19 | let pivot = array[highest] 20 | var i = lowest 21 | 22 | for j in lowest.. Int { 20 | let pivot = array[lowest] 21 | var i = lowest - 1 22 | var j = highest + 1 23 | 24 | while true { 25 | i += 1 26 | 27 | while array[i] < pivot { i += 1 } 28 | j -= 1 29 | 30 | while array[j] > pivot {j -= 1 } 31 | if i >= j { return j } 32 | 33 | array.swapAt(i, j) 34 | } 35 | } 36 | 37 | } 38 | 39 | //: Usage 40 | 41 | 42 | var nums = [1, 5, 6, 3, 2, 7, 8, 5, 8, 4, 2, 9, 0] 43 | Array.quickSort(array: &nums, lowest: 0, highest: nums.count - 1) 44 | 45 | print(#function + " sorted array : " , nums) 46 | 47 | //: [Next](@next) 48 | -------------------------------------------------------------------------------- /Search.playground/Pages/Binary.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: Playground - noun: a place where people can play 2 | 3 | import Foundation 4 | 5 | extension Array where Element: Comparable { 6 | 7 | func binarySearch(key: Element) -> Int? { 8 | var lowerBound = 0 9 | var upperBound = self.count - 1 10 | 11 | while lowerBound <= upperBound { 12 | let midIndex = lowerBound + (upperBound - lowerBound) / 2 13 | 14 | if self[midIndex] > key { 15 | upperBound = midIndex - 1 16 | } else if self[midIndex] < key { 17 | lowerBound = midIndex + 1 18 | } else { 19 | return midIndex 20 | } 21 | } 22 | 23 | return nil 24 | } 25 | 26 | } 27 | 28 | //: Usage 29 | 30 | // NOTES: 31 | // The following comparison will not work because two Strings cannot be compared with > or < operators - in order to determine lexical order they need to be lexically comapred. You can use the following construction to achieve such result: 32 | // strings.first?.compare(strings.last) == ComparisonResult.orderedAscending 33 | 34 | // Another solution is to implement custom coparison closure and inject it to the binarySearch function (this is TODO thing) 35 | // And the last approach is to override exisiting comparison operator for String type so two strings will be lexically compared with each other 36 | 37 | let strings = ["Steve", "Apple", "bar", "foo", "juce", "iPad", "macOS", "book", "universal"] 38 | var index = strings.binarySearch(key: "universal") 39 | 40 | let numbers = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67] 41 | index = numbers.binarySearch(key: 67) 42 | 43 | //: [Next](@next) 44 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | .DS_Store 6 | *.DS_Store 7 | 8 | ## Build generated 9 | build/ 10 | DerivedData/ 11 | 12 | ## Various settings 13 | *.pbxuser 14 | !default.pbxuser 15 | *.mode1v3 16 | !default.mode1v3 17 | *.mode2v3 18 | !default.mode2v3 19 | *.perspectivev3 20 | !default.perspectivev3 21 | xcuserdata/ 22 | 23 | ## Other 24 | *.moved-aside 25 | *.xccheckout 26 | *.xcscmblueprint 27 | 28 | ## Obj-C/Swift specific 29 | *.hmap 30 | *.ipa 31 | *.dSYM.zip 32 | *.dSYM 33 | 34 | ## Playgrounds 35 | timeline.xctimeline 36 | playground.xcworkspace 37 | 38 | # Swift Package Manager 39 | # 40 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 41 | # Packages/ 42 | # Package.pins 43 | .build/ 44 | 45 | # CocoaPods 46 | # 47 | # We recommend against adding the Pods directory to your .gitignore. However 48 | # you should judge for yourself, the pros and cons are mentioned at: 49 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 50 | # 51 | # Pods/ 52 | 53 | # Carthage 54 | # 55 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 56 | # Carthage/Checkouts 57 | 58 | Carthage/Build 59 | 60 | # fastlane 61 | # 62 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 63 | # screenshots whenever they are needed. 64 | # For more information about the recommended setup visit: 65 | # https://docs.fastlane.tools/best-practices/source-control/#source-control 66 | 67 | fastlane/report.xml 68 | fastlane/Preview.html 69 | fastlane/screenshots 70 | fastlane/test_output 71 | -------------------------------------------------------------------------------- /String Search.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | //: Playground - noun: a place where people can play 2 | 3 | import Foundation 4 | 5 | /*: 6 | Brute-Force string search implementation as String extension: 7 | */ 8 | 9 | extension String { 10 | 11 | func indexOf(_ string: String) -> String.Index? { 12 | for i in self.indices { 13 | var j = i 14 | var found = true 15 | 16 | for k in string.indices { 17 | if j == self.endIndex || self[j] != string[k] { 18 | found = false 19 | break 20 | } else { 21 | j = self.index(after: j) 22 | } 23 | } 24 | if found { 25 | return i 26 | } 27 | } 28 | return nil 29 | } 30 | 31 | } 32 | 33 | //: Usage 34 | 35 | let string = "😄😂💩🤦‍♂️🤖🤯💩" 36 | let pattern = "💩" 37 | 38 | let index = string.indexOf(pattern) 39 | print("Found index for \(pattern): " , index as Any) 40 | 41 | 42 | 43 | /*: 44 | Brute-Force string search implementation as String extension (using range(of:) method) which is much faster than the previous implementation: 45 | */ 46 | 47 | extension String { 48 | 49 | func indicesOf(_ string: String) -> [Int]? { 50 | 51 | var indices = [Int]() 52 | var position = self.startIndex 53 | 54 | while let range = range(of: string, options: String.CompareOptions.caseInsensitive, range: position ..< self.endIndex, locale: nil) { 55 | indices.append(distance(from: self.startIndex, to: range.lowerBound)) 56 | position = index(after: range.lowerBound) 57 | } 58 | 59 | return indices 60 | } 61 | } 62 | 63 | //: Usage: 64 | 65 | let indices = string.indicesOf(pattern) 66 | print("Found index for \(pattern): " , indices as Any) 67 | 68 | 69 | -------------------------------------------------------------------------------- /Data Structures.playground/Sources/PriorityQueue.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | /// Implementation of Priority Queue based on Heap data structure. 4 | public struct PriorityQueue where T: Comparable { 5 | 6 | // MARK: - Propertioes 7 | 8 | fileprivate var heap: Heap 9 | 10 | // MARK: - Initializers 11 | 12 | public init(order: @escaping (T, T) -> Bool) { 13 | heap = Heap(order: order) 14 | } 15 | 16 | public init(elements: [T], order: @escaping (T, T) -> Bool) { 17 | heap = Heap(array: elements, order: order) 18 | } 19 | 20 | // MARK: - Methods 21 | 22 | public func isEmpty() -> Bool { 23 | return heap.isEmpty 24 | } 25 | 26 | public func count() -> Int { 27 | return heap.count 28 | } 29 | 30 | public func peek() -> T? { 31 | return heap.peek() 32 | } 33 | 34 | public mutating func enqueue(_ element: T) { 35 | heap.insert(node: element) 36 | } 37 | 38 | public mutating func dequeue() -> T? { 39 | return heap.remove() 40 | } 41 | 42 | public mutating func changePriority(index i: Int, value: T) { 43 | return heap.replace(elementAt: i, with: value) 44 | } 45 | } 46 | 47 | extension PriorityQueue where T: Equatable { 48 | 49 | // MARK: - Methods 50 | 51 | public func index(of element: T) -> Int? { 52 | return heap.index(of: element) 53 | } 54 | } 55 | 56 | extension PriorityQueue: ExpressibleByArrayLiteral { 57 | 58 | // MARK: - Initializers 59 | 60 | public init(arrayLiteral elements: T...) { 61 | self.init(elements: elements, order: >) 62 | } 63 | } 64 | 65 | extension PriorityQueue: CustomStringConvertible, CustomDebugStringConvertible { 66 | 67 | // MARK: - Properties 68 | 69 | public var description: String { 70 | return heap.description 71 | } 72 | 73 | public var debugDescription: String { 74 | return heap.debugDescription 75 | } 76 | 77 | } 78 | 79 | -------------------------------------------------------------------------------- /Data Structures.playground/Pages/Tree.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: [Previous](@previous) 2 | 3 | import Foundation 4 | 5 | class Node { 6 | 7 | // MARK: - Properties 8 | 9 | weak var parent: Node? 10 | var value: T 11 | var children: [Node] = [] 12 | 13 | // MARK: - Initializers 14 | 15 | init(value: T) { 16 | self.value = value 17 | } 18 | 19 | // MARK: - Methods 20 | 21 | 22 | func insert(child: Node) { 23 | children.append(child) 24 | child.parent = self 25 | } 26 | 27 | func search(value: T) -> Node? { 28 | if value == self.value { 29 | return self 30 | } 31 | 32 | for child in children { 33 | if let foundChild = child.search(value: value) { 34 | return foundChild 35 | } 36 | } 37 | return nil 38 | } 39 | 40 | } 41 | 42 | extension Node: CustomStringConvertible, CustomDebugStringConvertible { 43 | 44 | // MARK: - Overriden properties 45 | 46 | var description: String { 47 | return prepareStringConvertable() 48 | } 49 | 50 | var debugDescription: String { 51 | return prepareStringConvertable() 52 | } 53 | 54 | // MARK: - Methods 55 | 56 | func prepareStringConvertable() -> String { 57 | var desc = "\(value)" 58 | 59 | if !children.isEmpty { 60 | desc += " {" + children.map { $0.description }.joined(separator: ", ") + "}" 61 | } 62 | return desc 63 | } 64 | } 65 | 66 | 67 | //: Usage 68 | 69 | let nodeApple = Node(value: "Apple") 70 | let nodeFoo = Node(value: "Foo") 71 | let nodeBar = Node(value: "Bar") 72 | let nodeChair = Node(value: "Chair") 73 | 74 | nodeApple.insert(child: nodeFoo) 75 | nodeApple.insert(child: nodeBar) 76 | nodeBar.insert(child: nodeChair) 77 | 78 | print(nodeApple) 79 | 80 | 81 | let foundNode = nodeApple.search(value: "Bar") 82 | print(foundNode) 83 | 84 | //: [Next](@next) 85 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at astemireleev@gmail.com. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: http://contributor-covenant.org 46 | [version]: http://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /Sort.playground/Pages/Merge.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: [Previous](@previous) 2 | 3 | /*: 4 | Merge sort is a sorting algorithm that has lower order running time than the insertion sort algorithm. Conceptually it is a devide and conquer sorting algorithm. 5 | 6 | The algorithm works by using recursion - it will divide an unsorted list into two parts - this is Divide part. 7 | 8 | The next phaze is Conquer - it recursively sorts sublists and if they are small enough then solve their base case. 9 | 10 | Base case is a situation when a list has a single item or it is empty. 11 | 12 | The final phase is Conbine - it merges the sorted sublists into a sorted sequence and return the elements back. 13 | */ 14 | 15 | /*: 16 | The following implementation is based on Arrays and is significantly slower than alternative Linked-List implementation. The performance difference increases with the number of elements to be sorted. 17 | */ 18 | 19 | // MARK: - Array extension that adds support for Merge sort algorithm 20 | extension Array where Element: Comparable { 21 | 22 | // MARK: - Typealiases 23 | 24 | public typealias ComparisonClosure = (Element, Element) -> Bool 25 | 26 | // MARK: - Methods 27 | 28 | /// Sorts an array of Comparable elements using Merge sort algorithm 29 | /// 30 | /// - Parameter list: is an Array of Comparable elements 31 | /// - Returns: the same sorted Array 32 | public static func mergeSort(_ list: [Element], order sign: ComparisonClosure) -> [Element] { 33 | 34 | if list.count < 2 { return list } 35 | let center = (list.count) / 2 36 | 37 | let leftMergeSort = mergeSort([Element](list[0.. [Element] { 50 | 51 | var leftIndex = 0 52 | var rightIndex = 0 53 | let totalCapacity = lhalf.count + rhalf.count 54 | 55 | var temp = [Element]() 56 | temp.reserveCapacity(totalCapacity) 57 | 58 | while leftIndex < lhalf.count && rightIndex < rhalf.count { 59 | let leftElement = lhalf[leftIndex] 60 | let rightElement = rhalf[rightIndex] 61 | 62 | let leftGreatherThanRight = sign(leftElement, rightElement) 63 | let leftSmallerThanRight = !leftGreatherThanRight 64 | 65 | if leftGreatherThanRight { 66 | temp.append(leftElement) 67 | leftIndex += 1 68 | } else if leftSmallerThanRight { 69 | temp.append(rightElement) 70 | rightIndex += 1 71 | } else { 72 | temp.append(leftElement) 73 | temp.append(rightElement) 74 | leftIndex += 1 75 | rightIndex += 1 76 | } 77 | } 78 | 79 | temp += [Element](lhalf[leftIndex.. { 6 | // MARK: - Private properties 7 | 8 | private var data: [T] 9 | 10 | // MARK: - Public properties 11 | 12 | public var count: Int { 13 | return data.count 14 | } 15 | 16 | public var capacity: Int { 17 | get { 18 | return data.capacity 19 | } 20 | set { 21 | data.reserveCapacity(capacity) 22 | } 23 | } 24 | 25 | // MARK: - Initializers 26 | 27 | public init() { 28 | data = [] 29 | } 30 | 31 | public init(_ elements: S) where S.Iterator.Element == T { 32 | self.init() 33 | data.append(contentsOf: elements) 34 | } 35 | 36 | // MARK: - Methods 37 | 38 | public mutating func dequeueFront() -> T? { 39 | return data.removeFirst() 40 | } 41 | 42 | public mutating func dequeueBack() -> T? { 43 | return data.removeLast() 44 | } 45 | 46 | public mutating func enqueue(front element: T) { 47 | data.insert(element, at: 0) 48 | } 49 | 50 | public mutating func enqueue(back element: T) { 51 | data.append(element) 52 | } 53 | 54 | public mutating func clear() { 55 | data.removeAll() 56 | } 57 | 58 | public mutating func isFull() -> Bool { 59 | return count == data.capacity 60 | } 61 | 62 | public func isEmpty() -> Bool { 63 | return data.isEmpty 64 | } 65 | 66 | public func peekFirst() -> T? { 67 | return data.first 68 | } 69 | 70 | public func peekLast() -> T? { 71 | return data.last 72 | } 73 | 74 | // MARK: - Private methods 75 | 76 | private func checkIndex(index: Int) throws { 77 | if index < 0 || index > count { 78 | throw DequeueError.indexOutOfRange 79 | } 80 | } 81 | } 82 | 83 | enum DequeueError: Error { 84 | case indexOutOfRange 85 | } 86 | 87 | extension Dequeue: CustomStringConvertible, CustomDebugStringConvertible { 88 | 89 | public var description: String { 90 | return data.description 91 | } 92 | 93 | public var debugDescription: String { 94 | return data.description 95 | } 96 | } 97 | 98 | extension Dequeue: ExpressibleByArrayLiteral { 99 | 100 | public init(arrayLiteral elements: T...) { 101 | self.init(elements) 102 | } 103 | } 104 | 105 | extension Dequeue: Sequence { 106 | 107 | public func makeIterator() -> AnyIterator { 108 | let indexedIterator = IndexingIterator(_elements: data.lazy) 109 | return AnyIterator(indexedIterator) 110 | } 111 | 112 | public func generate() -> AnyIterator { 113 | let indexingIteratoer = IndexingIterator(_elements: data.lazy) 114 | return AnyIterator(indexingIteratoer) 115 | 116 | } 117 | } 118 | 119 | 120 | extension Dequeue: MutableCollection { 121 | 122 | // MARK: - Core protocol conformance 123 | 124 | public var startIndex: Int { 125 | return 0 126 | } 127 | 128 | public var endIndex: Int { 129 | return count - 1 130 | } 131 | 132 | public func index(after i: Int) -> Int { 133 | return data.index(after: i) 134 | } 135 | 136 | // MARK: - Subscript implementation 137 | 138 | public subscript(index: Int) -> T { 139 | get { 140 | checkHandledIndex(index: index) 141 | return data[index] 142 | } 143 | set { 144 | checkHandledIndex(index: index) 145 | data[index] = newValue 146 | } 147 | } 148 | 149 | // MARK: - Utility method 150 | 151 | private func checkHandledIndex(index: Int) { 152 | do { 153 | try checkIndex(index: index) 154 | } catch { 155 | fatalError(error.localizedDescription) 156 | } 157 | } 158 | } 159 | 160 | //: Usage: 161 | 162 | var dequeue = Dequeue([1,2,34,5,6,7]) 163 | let back = dequeue.dequeueBack() 164 | let front = dequeue.dequeueFront() 165 | 166 | dequeue.enqueue(front: 99) 167 | dequeue.enqueue(back: 99) 168 | 169 | //: [Next](@next) 170 | -------------------------------------------------------------------------------- /Data Structures.playground/Pages/Linked List.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: [Previous](@previous) 2 | 3 | public struct LinkedList { 4 | 5 | // MARK: - Private properties 6 | 7 | fileprivate var head: Node? 8 | fileprivate var _count: Int = 0 9 | 10 | // MARK: - Public properties 11 | 12 | public var count: Int { 13 | return _count 14 | } 15 | 16 | // MARK: - Initializers 17 | 18 | public init() { 19 | head = nil 20 | } 21 | 22 | public init(sequence: S) where S.Iterator.Element == T { 23 | for element in sequence { 24 | push(element: element) 25 | } 26 | } 27 | 28 | // MARK: - API Methods 29 | 30 | public mutating func push(element: T) { 31 | let node = Node(data: element) 32 | node.next = head 33 | head = node 34 | _count += 1 35 | } 36 | 37 | public mutating func pop() -> T? { 38 | if isEmpty() { return nil } 39 | 40 | let element = head?.data 41 | head = head?.next 42 | _count -= 1 43 | 44 | return element 45 | } 46 | 47 | public func peek() -> T? { 48 | return head?.data 49 | } 50 | 51 | public func isEmpty() -> Bool { 52 | return _count == 0 53 | } 54 | 55 | } 56 | 57 | /// Node class represents a generic node element that is used to construct resursuve linked list 58 | private class Node { 59 | fileprivate var next: Node? 60 | fileprivate var data: T 61 | 62 | init(data: T) { 63 | next = nil 64 | self.data = data 65 | } 66 | } 67 | 68 | // MARK: - Conformance to CusomStringConvertable and CustomDebugStringConvertable protocols 69 | extension LinkedList: CustomStringConvertible, CustomDebugStringConvertible { 70 | 71 | // MARK: - Conformance to the protocols 72 | 73 | public var description: String { 74 | return composeDescription() 75 | } 76 | 77 | public var debugDescription: String { 78 | return composeDescription() 79 | } 80 | 81 | // MARK: - Utliity mehtods 82 | 83 | private func composeDescription() -> String { 84 | var description = "[" 85 | var lastNode = head 86 | 87 | while lastNode != nil { 88 | description += "\(String(describing: lastNode?.data))" 89 | lastNode = lastNode?.next 90 | 91 | if lastNode != nil { 92 | description += "," 93 | } 94 | } 95 | description += "]" 96 | 97 | return description 98 | } 99 | 100 | } 101 | 102 | 103 | // MARK: - Conformance to ExpressibleByArrayLiteral protocol that allows to treat the LinkedList as an array when initializing it 104 | extension LinkedList: ExpressibleByArrayLiteral { 105 | 106 | public init(arrayLiteral elements: T...) { 107 | for element in elements { 108 | push(element: element) 109 | } 110 | } 111 | } 112 | 113 | /// Custom generic struct that conforms to IteratorProtocol. The structure receives an instnace of head during initialization pohase that allows us to iterate through the elements in the linked ilst. 114 | public struct LinkedListIterator: IteratorProtocol { 115 | 116 | // MARK: - Properties 117 | 118 | public typealias Element = T 119 | private var head: Node? 120 | 121 | // MARK: - Initializers 122 | 123 | fileprivate init(head: Node?) { 124 | self.head = head 125 | } 126 | 127 | // MARK: - Conformacne to the protocol 128 | 129 | public mutating func next() -> T? { 130 | guard let uHead = head else { return nil } 131 | let item = uHead.data 132 | head = uHead.next 133 | return item 134 | } 135 | } 136 | 137 | // MARK: - Conformance to 138 | extension LinkedList: Sequence { 139 | public typealias Iterator = LinkedListIterator 140 | 141 | public func makeIterator() -> LinkedListIterator { 142 | return LinkedListIterator(head: head) 143 | } 144 | } 145 | 146 | 147 | // MARK: - Usage 148 | 149 | var list: LinkedList = [1,2,4,5] 150 | debugPrint(list) 151 | 152 | list.pop() 153 | debugPrint(list) 154 | 155 | var newList = LinkedList(sequence: list) 156 | debugPrint(newList) 157 | newList.pop() 158 | debugPrint(newList) 159 | 160 | 161 | //: [Next](@next) 162 | -------------------------------------------------------------------------------- /Data Structures.playground/Pages/CircularBuffer.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: [Previous](@previous) 2 | 3 | import Foundation 4 | 5 | public struct CircularBuffer { 6 | fileprivate var data: [T] 7 | fileprivate var head: Int = 0, tail: Int = 0 8 | private var internalCount: Int = 0 9 | private var overwriteOperation: CircularBufferOperation = CircularBufferOperation.overwrite 10 | 11 | public init() { 12 | data = [T]() 13 | data.reserveCapacity(Constants.defaultBufferCapacity) 14 | } 15 | 16 | public init(_ count: Int, overwriteOperation: CircularBufferOperation = .overwrite) { 17 | var capacity = count 18 | if (capacity < 1) { 19 | capacity = Constants.defaultBufferCapacity 20 | 21 | } 22 | if ((capacity & (~capacity + 1)) != capacity) { 23 | var b = 1 24 | while (b < capacity) { 25 | b = b << 1 26 | 27 | } 28 | capacity = b 29 | 30 | } 31 | data = [T]() 32 | data.reserveCapacity(capacity) 33 | self.overwriteOperation = overwriteOperation 34 | } 35 | 36 | public init(_ elements: S, size: Int) where S.Iterator.Element == T { 37 | self.init(size) 38 | elements.forEach({ push(element: $0) }) 39 | 40 | } 41 | 42 | public mutating func pop() -> T? { 43 | if isEmpty() { 44 | return nil 45 | 46 | } 47 | let el = data[head] 48 | head = incrementPointer(pointer: head) 49 | internalCount -= 1 50 | return el 51 | 52 | } 53 | 54 | public func peek() -> T? { 55 | if isEmpty() { 56 | return nil 57 | 58 | } 59 | return data[head] 60 | 61 | } 62 | 63 | public mutating func push(element: T) { 64 | if isFull() { 65 | switch(overwriteOperation) { 66 | case .ignore: 67 | return 68 | case .overwrite: 69 | pop() 70 | } 71 | } 72 | if (data.endIndex < data.capacity) { 73 | data.append(element) 74 | 75 | } else { 76 | data[tail] = element 77 | 78 | } 79 | tail = incrementPointer(pointer: tail) 80 | internalCount += 1 81 | } 82 | 83 | public mutating func clear() { 84 | head = 0 85 | tail = 0 86 | internalCount = 0 87 | data.removeAll(keepingCapacity: true) 88 | 89 | } 90 | 91 | public var count: Int { 92 | return internalCount 93 | 94 | } 95 | 96 | public var capacity: Int { 97 | get { 98 | return data.capacity 99 | } set { 100 | data.reserveCapacity(newValue) 101 | } 102 | } 103 | 104 | public func isFull() -> Bool { 105 | return count == data.capacity 106 | } 107 | 108 | public func isEmpty() -> Bool { 109 | return (count < 1) 110 | } 111 | 112 | fileprivate func incrementPointer(pointer: Int) -> Int { 113 | return (pointer + 1) & (data.capacity - 1) 114 | } 115 | 116 | fileprivate func decrementPointer(pointer: Int) -> Int { 117 | return (pointer - 1) & (data.capacity - 1) 118 | } 119 | 120 | fileprivate func convertLogicalToRealIndex(logicalIndex: Int) -> Int { 121 | return (head + logicalIndex) & (data.capacity - 1) 122 | } 123 | 124 | fileprivate func checkIndex(index: Int) { 125 | if index < 0 || index > count { 126 | fatalError("Index out of range") 127 | 128 | } 129 | } 130 | 131 | } 132 | 133 | private struct Constants { 134 | fileprivate static let defaultBufferCapacity:Int = 16 135 | 136 | } 137 | 138 | public enum CircularBufferOperation { 139 | case ignore, overwrite 140 | } 141 | 142 | extension CircularBuffer: CustomStringConvertible, CustomDebugStringConvertible { 143 | public var description: String { 144 | return data.description 145 | } 146 | 147 | public var debugDescription: String { 148 | return data.debugDescription 149 | } 150 | } 151 | 152 | extension CircularBuffer: Sequence { 153 | public func makeIterator() -> AnyIterator { 154 | var newData = [T]() 155 | 156 | if count > 0 { 157 | newData = [T](repeating: data[head], count: count) 158 | if head > tail { 159 | let front = data.capacity - head 160 | newData[0.. where T : Comparable { 8 | 9 | // MARK: - Cases 10 | 11 | case empty 12 | indirect case node(BinaryTree, T, BinaryTree) 13 | 14 | // MARK: - Properties 15 | 16 | var count: Int { 17 | switch self { 18 | case let .node(left, _, right): 19 | return left.count + 1 + right.count 20 | case .empty: 21 | return 0 22 | } 23 | } 24 | 25 | var isEmpty: Bool { 26 | switch self { 27 | case .empty: 28 | return true 29 | case .node(_, _, _): 30 | return false 31 | } 32 | } 33 | 34 | var elements: [T] { 35 | switch self { 36 | case .empty: 37 | return [] 38 | case .node(let left, let element, let right): 39 | return left.elements + [element] + right.elements 40 | } 41 | } 42 | 43 | var height: Int { 44 | switch self { 45 | case .empty: 46 | return 0 47 | case .node(let left, let _ , let right): 48 | return 1 + max(left.height, right.height) 49 | } 50 | } 51 | 52 | // MARK: - Static methods 53 | 54 | static func contains(element: T, tree: BinaryTree) -> Bool { 55 | switch tree { 56 | case .empty: 57 | return false 58 | case .node(let left, let item, let right): 59 | if element < item { 60 | return contains(element: element, tree: left) 61 | } else if element > item { 62 | return contains(element: element, tree: right) 63 | } 64 | return true 65 | } 66 | } 67 | 68 | 69 | static func constructEmpty() -> BinaryTree { 70 | return .empty 71 | } 72 | } 73 | 74 | //: There are several ways how Binary Trees can be constructed. Classic approach is to use reference types e.g. classes. The implementation is pretty similar to the Tree data structure that is implemented in this playground file. However we used a different approach - value types... well sort of. Swift has several value types: structures (aka struct) and enumeration types (aka enum). In Swift enumeration type has many cool features and their practical application usage is far more extended and advanced in comparison to Java or Objective C for example. You may think that it sounds a bit weird... and you will be right. Value types have fixed size, meaning that compiler knows in advance the size of a struct or enum type. However when building recursion compiler does not have information about the size of the type. The last two mentioned conditions contradict each other, so we need to tell the compiler about our intentions. Swift has such mechanims and a special keyword called `indirect`. `Indirect` keyword introduces a layer of indirection behind the scenes which allows us to use value cases or whole enumeration types recursiveley. You need to note that this keyword only works with enums. Structs do not support this feature. 75 | 76 | 77 | //: Lets conform to CustomStringConvertible and CustomDebugStringConvirtable protocols, which greathly reduces complexity of degugging and inspecting the trees. 78 | 79 | extension BinaryTree: CustomStringConvertible, CustomDebugStringConvertible { 80 | 81 | // MARK: - Conformance to CustomStringCovertible and CustomDebugStringConvertible protocols 82 | 83 | var description: String { 84 | return cnstructDebugInfo() 85 | } 86 | 87 | var debugDescription: String { 88 | return cnstructDebugInfo() 89 | } 90 | 91 | // MARK: - Private helpers 92 | 93 | private func cnstructDebugInfo() -> String { 94 | switch self { 95 | case let .node(left, value, right): 96 | return """ 97 | \t value: \(value), 98 | \t left = [ \(left.description) ], 99 | \t right = [ \(right.description) ] 100 | """ 101 | case .empty: 102 | return """ 103 | \t 104 | """ 105 | } 106 | } 107 | } 108 | 109 | //: Usage: 110 | 111 | //: The following expression: 2 + 3 * 5 can be easility represented by Binary Tree data structre using the following construction: 112 | 113 | let tree5 = BinaryTree.node(.empty, "5", .empty) 114 | let tree3 = BinaryTree.node(.empty, "3", .empty) 115 | let treeMult = BinaryTree.node(tree5, "*", tree3) 116 | 117 | let tree2 = BinaryTree.node(.empty, "2", .empty) 118 | let finalTree = BinaryTree.node(tree2, "+", treeMult) 119 | 120 | print(finalTree) 121 | 122 | let containsThree = BinaryTree.contains(element: "3", tree: finalTree) 123 | print(containsThree) 124 | 125 | let containsFour = BinaryTree.contains(element: "4", tree: finalTree) 126 | print(containsFour) 127 | 128 | let treeElementsArray = finalTree.elements 129 | print(treeElementsArray) 130 | 131 | print(finalTree.height) 132 | 133 | //: In order to properly construct Binary Tree you need to start building the tree by layers. The means the first thing you need to do is to draw the tree and split every single operation into separate layer. Multiplying 5 and 3 is the first operation that happens, that is why we compose this operation separately and form a Binary Tree node separately. Then we define 2 as a separate leaf node and add it to the first layer, which results into a final tree node that contains all the nodes. 134 | 135 | //: [Next](@next) 136 | -------------------------------------------------------------------------------- /Data Structures.playground/Pages/DoublyLinkedList.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: [Previous](@previous) 2 | 3 | import Foundation 4 | 5 | private class DoublyNode: CustomStringConvertible, CustomDebugStringConvertible { 6 | 7 | // MARK: - Conformances to CustomStringConvertible & CustomDebugStringConvertible protocols 8 | 9 | var description: String { 10 | return representation() 11 | } 12 | 13 | var debugDescription: String { 14 | return representation() 15 | } 16 | 17 | private func representation() -> String { 18 | return "\(String(describing: data))" 19 | } 20 | 21 | // MARK: - Properties 22 | 23 | fileprivate var next: DoublyNode? 24 | fileprivate var data: T 25 | fileprivate var previous: DoublyNode? 26 | 27 | // MARK: - Initailziers 28 | 29 | init(data: T, next: DoublyNode?, previous: DoublyNode?) { 30 | self.data = data 31 | self.next = next 32 | self.previous = previous 33 | } 34 | } 35 | 36 | public struct DoublyLinkedList { 37 | 38 | // MARK: - Fileprivate properties 39 | 40 | fileprivate var head: DoublyNode? 41 | fileprivate var tail: DoublyNode? 42 | fileprivate var current: DoublyNode? 43 | 44 | fileprivate var _count: Int = 0 45 | 46 | // MARK: - Public properties 47 | 48 | public var count: Int { 49 | return _count 50 | } 51 | 52 | public var front: T? { 53 | return head?.data 54 | } 55 | 56 | public var back: T? { 57 | return tail?.data 58 | } 59 | 60 | public var isEmpty: Bool { 61 | return count == 0 62 | } 63 | 64 | // MARK: - Initailizers 65 | 66 | public init() { 67 | head = nil 68 | tail = nil 69 | current = nil 70 | } 71 | 72 | public init(sequence: S) where S.Iterator.Element == T { 73 | for element in sequence { 74 | push(newTail: element) 75 | } 76 | } 77 | 78 | // MARK: - Methods 79 | 80 | public mutating func next() -> T? { 81 | current = current?.next 82 | return current?.data 83 | } 84 | 85 | public mutating func previous() -> T? { 86 | current = current?.previous 87 | return current?.data 88 | } 89 | 90 | public mutating func push(newHead element: T) { 91 | var node: DoublyNode 92 | 93 | if count == 0 { 94 | node = DoublyNode(data: element, next: nil, previous: nil) 95 | tail = node 96 | } else { 97 | node = DoublyNode(data: element, next: head, previous: nil) 98 | head?.previous = node 99 | } 100 | 101 | head = node 102 | _count += 1 103 | 104 | if count == 1 { 105 | current = head 106 | } else if head === current { 107 | current = head?.next 108 | } 109 | } 110 | 111 | public mutating func push(newTail element: T) { 112 | var node: DoublyNode 113 | 114 | if count == 0 { 115 | node = DoublyNode(data: element, next: nil, previous: nil) 116 | head = node 117 | } else { 118 | node = DoublyNode(data: element, next: nil, previous: tail) 119 | tail?.next = node 120 | } 121 | 122 | tail = node 123 | _count += 1 124 | 125 | if count == 1 { 126 | current = tail 127 | } else if tail === current { 128 | current = tail?.previous 129 | } 130 | } 131 | 132 | @discardableResult 133 | public mutating func removeHead() -> T? { 134 | if isEmpty { return nil } 135 | 136 | let element = head?.data 137 | head = head?.next 138 | _count -= 1 139 | 140 | return element 141 | } 142 | 143 | public mutating func removeTail() -> T? { 144 | if isEmpty { return nil } 145 | 146 | let element = tail?.data 147 | tail = tail?.previous 148 | _count -= 1 149 | return element 150 | } 151 | 152 | // MARK: - Private methods 153 | 154 | private mutating func reset() { 155 | head = nil 156 | tail = nil 157 | current = nil 158 | } 159 | } 160 | 161 | extension DoublyLinkedList: CustomStringConvertible, CustomDebugStringConvertible { 162 | 163 | // MARK: - Conformances to CustomStringConvertible & CustomDebugStringConvertible protocols 164 | 165 | public var description:String { 166 | return representation() 167 | } 168 | 169 | public var debugDescription:String { 170 | return representation() 171 | } 172 | 173 | // MARK: - Private methods 174 | 175 | private func representation() -> String { 176 | var output = "(" 177 | var c = 0 178 | var x = head 179 | 180 | while nil !== x { 181 | output += "\(String(describing: x))" 182 | c += 1 183 | if c != count { 184 | output += ", " 185 | } 186 | x = x!.next 187 | } 188 | output += ")" 189 | return output 190 | } 191 | } 192 | 193 | extension DoublyLinkedList: ExpressibleByArrayLiteral { 194 | 195 | public init(arrayLiteral elements: T...) { 196 | for element in elements { 197 | push(newTail: element) 198 | } 199 | } 200 | } 201 | 202 | extension DoublyLinkedList: Sequence { 203 | 204 | public func makeIterator() -> AnyIterator { 205 | var iterator = head 206 | 207 | return AnyIterator { 208 | defer { iterator = iterator?.next } 209 | return iterator?.data 210 | } 211 | } 212 | 213 | } 214 | 215 | //: Usage: 216 | 217 | var list: DoublyLinkedList = [1,2,4,5,6,7] 218 | print(list) 219 | 220 | list.head 221 | list.tail 222 | 223 | list.removeHead() 224 | print(list) 225 | 226 | list.removeTail() 227 | print(list) 228 | 229 | list.push(newHead: 99) 230 | print(list) 231 | 232 | list.push(newTail: 101) 233 | print(list) 234 | 235 | //: [Next](@next) 236 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # swift-algorithms-data-structs [![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/sindresorhus/awesome) 2 | 3 | [![Language](https://img.shields.io/badge/language-Swift_5.3-orange.svg)]() 4 | [![License](https://img.shields.io/badge/license-MIT-blue.svg)]() 5 | 6 | **Last Update: 18/July/2021.** 7 | 8 | ![](https://github.com/jVirus/swift-algorithms-data-structs/blob/master/cover-algo-datastruct.png) 9 | 10 | ### If you like the project, please give it a star ⭐ It will show the creator your appreciation and help others to discover the repo. 11 | 12 | # ✍️ About 13 | The repository contains examples of common `Algorithms` and `Data Structures` implemented using the latest stable version of `Swift`programming language. The development attempts to fully utilize `Standard Library` and `Protocol-Oriented` paradigm. The project provides interactive visualizations using `SpriteKit` and `SceneKit` frameworks (in development). 14 | 15 | ## 📚 Content 16 | Each major section will be wrapped into a separete `.playground` file, meaning that `data structures` and `algorithms` will be separeted for convenient management. :octocat: 17 | 18 | - [About](#about) 19 | - [Data Structures](https://github.com/jVirus/swift-algorithms-data-structs/tree/master/Data%20Structures.playground/Pages) 20 | - [Stack](https://github.com/jVirus/swift-algorithms-data-structs/blob/master/Data%20Structures.playground/Pages/Stack.xcplaygroundpage/Contents.swift) 21 | - [Circular Buffer](https://github.com/jVirus/swift-algorithms-data-structs/blob/master/Data%20Structures.playground/Pages/CircularBuffer.xcplaygroundpage/Contents.swift) 22 | - [Queue](https://github.com/jVirus/swift-algorithms-data-structs/blob/master/Data%20Structures.playground/Pages/Queue.xcplaygroundpage/Contents.swift) 23 | - [Priority Queue](https://github.com/jVirus/swift-algorithms-data-structs/blob/master/Data%20Structures.playground/Sources/PriorityQueue.swift) 24 | - [Dequeue](https://github.com/jVirus/swift-algorithms-data-structs/blob/master/Data%20Structures.playground/Pages/Dequeue.xcplaygroundpage/Contents.swift) 25 | - [Linked List](https://github.com/jVirus/swift-algorithms-data-structs/blob/master/Data%20Structures.playground/Pages/Linked%20List.xcplaygroundpage/Contents.swift) 26 | - [Doubly Linked List](https://github.com/jVirus/swift-algorithms-data-structs/blob/master/Data%20Structures.playground/Pages/DoublyLinkedList.xcplaygroundpage/Contents.swift) 27 | - Circular Linked List 28 | - [Tree](https://github.com/jVirus/swift-algorithms-data-structs/blob/master/Data%20Structures.playground/Pages/Tree.xcplaygroundpage/Contents.swift) 29 | - [Binary Tree](https://github.com/jVirus/swift-algorithms-data-structs/blob/master/Data%20Structures.playground/Pages/BinaryTree.xcplaygroundpage/Contents.swift) 30 | - [Binary Search Tree](https://github.com/jVirus/swift-algorithms-data-structs/blob/master/Data%20Structures.playground/Pages/BinarySearchTree.xcplaygroundpage/Contents.swift) 31 | - B-Tree 32 | - Splay Tree 33 | - [Red-Black Tree](https://github.com/jVirus/swift-algorithms-data-structs/blob/master/Data%20Structures.playground/Pages/Red-Black%20Tree.xcplaygroundpage/Contents.swift) 34 | - AVL Tree 35 | - Trie Tree 36 | - Radix Tree 37 | - Quad Tree 38 | - Octree 39 | - [Heap](https://github.com/jVirus/swift-algorithms-data-structs/blob/master/Data%20Structures.playground/Pages/Heap.xcplaygroundpage/Contents.swift) 40 | - [Graph](https://github.com/jVirus/swift-algorithms-data-structs/blob/master/Data%20Structures.playground/Pages/Graph.xcplaygroundpage/Contents.swift) 41 | - Algorithms 42 | - [Sort](https://github.com/jVirus/swift-algorithms-data-structs/tree/master/Sort.playground/Pages) 43 | - [Insertion](https://github.com/jVirus/swift-algorithms-data-structs/blob/master/Sort.playground/Pages/Insertion.xcplaygroundpage/Contents.swift) 44 | - [Merge](https://github.com/jVirus/swift-algorithms-data-structs/blob/master/Sort.playground/Pages/Merge.xcplaygroundpage/Contents.swift) 45 | - [Quick (Hoare's Partitioning Scheme)](https://github.com/jVirus/swift-algorithms-data-structs/blob/master/Sort.playground/Pages/Quick-Hoare-Scheme.xcplaygroundpage/Contents.swift) 46 | - [Quick (Lomuto's Partitioning Scheme)](https://github.com/jVirus/swift-algorithms-data-structs/blob/master/Sort.playground/Pages/Quick-Lomuto-Scheme.xcplaygroundpage/Contents.swift) 47 | - [Bubble](https://github.com/jVirus/swift-algorithms-data-structs/blob/master/Sort.playground/Pages/Bubble.xcplaygroundpage/Contents.swift) 48 | - [Heap](https://github.com/jVirus/swift-algorithms-data-structs/blob/master/Data%20Structures.playground/Sources/Heap.swift) 49 | - [Shell](https://github.com/jVirus/swift-algorithms-data-structs/blob/master/Sort.playground/Pages/Shell.xcplaygroundpage/Contents.swift) 50 | - [Radix](https://github.com/jVirus/swift-algorithms-data-structs/blob/master/Sort.playground/Pages/Radix.xcplaygroundpage/Contents.swift) 51 | - [Search](https://github.com/jVirus/swift-algorithms-data-structs/tree/master/Search.playground/Pages) 52 | - [Binary](https://github.com/jVirus/swift-algorithms-data-structs/blob/master/Search.playground/Pages/Binary.xcplaygroundpage/Contents.swift) 53 | - [Linear](https://github.com/jVirus/swift-algorithms-data-structs/blob/master/Search.playground/Pages/Linear.xcplaygroundpage/Contents.swift) 54 | - [Heap](https://github.com/jVirus/swift-algorithms-data-structs/blob/master/Data%20Structures.playground/Pages/Heap.xcplaygroundpage/Contents.swift) 55 | - [Tree](https://github.com/jVirus/swift-algorithms-data-structs/blob/master/Data%20Structures.playground/Pages/Tree.xcplaygroundpage/Contents.swift) 56 | - [String Search](https://github.com/jVirus/swift-algorithms-data-structs/tree/master/String%20Search.playground) 57 | - [Brute-Force](https://github.com/jVirus/swift-algorithms-data-structs/blob/master/String%20Search.playground/Contents.swift) 58 | - Boyer-Moore 59 | - Rabin-Karp 60 | - Finite automation 61 | - Knuth-Morris-Pratt 62 | - Graphs 63 | - Dijkstra's shortest path algorithm 64 | - Minimum Spanning Tree (unweighted tree) 65 | - Minimum Spanning Tree 66 | - Breadth-First Search (BFS) 67 | - Depth-First Search (DFS) 68 | - Shortest Path (unweighted tree) 69 | 70 | # 📺 Interactive Visualizations 71 | The following list contains interactive visualizations that were implemented as part of the project. In majority of cases you can interact with a `data structure` or `algorithm` through a number of `UI` controlls. Visualizations are implemented using `SpriteKit` framework and are intended to explain the work of `data structures` and `algorithms` in a more highlevel way, which is a great way to learn especially if you are a beginner or you aren't familiar with something listed in the repository. 72 | 73 | ## Stack 74 | Stack visalization has two main UI controlls: `push` and `pop`. They perform corresponding operations: adding a new book at the top of the stack and removing the top book from the stack. Also there is a label that shows how many books are in the stack. Since the screen size is limited and for the sake of simplicity, the number of books in stack is limited. 75 | 76 | 77 | 78 | ## Queue 79 | Queue visualization is similar to Stack interactive visualization: it has two main `UI` controls that allow you to interact with the interface of the `Queue` data structure for `enqueue` and `dequeue` operations. You can see the number of animals in the queue as well. 80 | 81 | 82 | 83 | 84 | # 👨‍💻 Author 85 | [Astemir Eleev](https://github.com/jVirus) 86 | 87 | # 🔖 Licence 88 | THE PROJECT IS UNDER [MIT](https://github.com/jVirus/iOS-Algo-Play-Book/blob/master/LICENSE) LICENCE 89 | -------------------------------------------------------------------------------- /Data Structures.playground/Pages/Graph.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: [Previous](@previous) 2 | 3 | import Foundation 4 | 5 | /// Notes: 6 | /// - Edge object is always directed - a one-way connection. In order to have two way connection, you need to add additional weight property and some indication that Edge represents a two-way connection. 7 | /// - If you want to have undirected connection - you also need to add an Edge object in the opposite direction. 8 | /// - Weight property can be ommited so in such case it will represent unweighted graph. 9 | /// - The arbitraty stored data is a generic type T which is Hashable to enforce uniqueness and also Equatable, so they can be equated. 10 | /// - One good approach is to have protocol that describes various types of Edges, so vertices can be connected by different types of Endges - protocol-oriented appraoch may be a good way to go. 11 | 12 | struct Edge { 13 | let from: Vertex 14 | let to: Vertex 15 | let weight: Double? 16 | } 17 | 18 | // MARK: - Adds conformance to Hashable protocol 19 | extension Edge: Hashable { 20 | func hash(into hasher: inout Hasher) { 21 | hasher.combine(from.hashValue) 22 | hasher.combine(to.hashValue) 23 | hasher.combine(weight.hashValue) 24 | } 25 | } 26 | 27 | extension Edge: CustomStringConvertible, CustomDebugStringConvertible { 28 | 29 | var description: String { 30 | return commonDescription 31 | } 32 | 33 | var debugDescription: String { 34 | return commonDescription 35 | } 36 | 37 | private var commonDescription: String { 38 | var weightDescription = "" 39 | 40 | if let weight = self.weight { 41 | weightDescription = "\(weight)" 42 | } else { 43 | weightDescription = "nil" 44 | } 45 | 46 | return "from: \(from), \t to: \(to), \t weight: \(weightDescription)" 47 | } 48 | } 49 | 50 | struct Vertex { 51 | var data: T 52 | let index: Int 53 | } 54 | 55 | // MARK: - Adds conformance to Hashable protocol 56 | extension Vertex: Hashable { 57 | var hashValue: Int { 58 | return data.hashValue ^ index.hashValue 59 | } 60 | func hash(into hasher: inout Hasher) { 61 | hasher.combine(data.hashValue) 62 | hasher.combine(index.hashValue) 63 | } 64 | } 65 | 66 | extension Vertex: CustomStringConvertible, CustomDebugStringConvertible { 67 | 68 | var description: String { 69 | return commonDescription 70 | } 71 | 72 | var debugDescription: String { 73 | return commonDescription 74 | } 75 | 76 | private var commonDescription: String { 77 | return "data: \(data), index: \(index)" 78 | } 79 | } 80 | 81 | // MARK: - Custom Operators 82 | 83 | func == (lhs: Vertex, rhs: Vertex) -> Bool { 84 | return lhs.data == rhs.data 85 | } 86 | 87 | func == (lhs: Edge, rhs: Edge) -> Bool { 88 | return lhs.from == rhs.from && lhs.to == rhs.to 89 | } 90 | 91 | // MARK: - AdjacencyList struct 92 | 93 | struct AdjacencyList { 94 | 95 | // MARK: - Properties 96 | 97 | let vertex: Vertex 98 | var edges: [Edge] = [] 99 | 100 | // MARK: - Initializers 101 | 102 | init(vertex: Vertex) { 103 | self.vertex = vertex 104 | } 105 | 106 | // MARK: - Methods 107 | 108 | mutating func add(edge: Edge) { 109 | if !edges.isEmpty { 110 | let equalEdges = edges.filter { $0 == edge } 111 | 112 | if !equalEdges.isEmpty { 113 | return 114 | } 115 | } 116 | edges.append(edge) 117 | } 118 | } 119 | 120 | extension AdjacencyList: CustomStringConvertible, CustomDebugStringConvertible { 121 | 122 | var description: String { 123 | return commonDecription 124 | } 125 | 126 | var debugDescription: String { 127 | return commonDecription 128 | } 129 | 130 | private var commonDecription: String { 131 | return """ 132 | \n 133 | vertex: \(vertex), 134 | edges: \(edges) 135 | """ 136 | } 137 | 138 | } 139 | 140 | struct AdjacencyListGraph { 141 | 142 | // MARK: - Properties 143 | 144 | var adjacencyLists: [AdjacencyList] = [] 145 | 146 | var vertices: [Vertex] { 147 | var vertices = [Vertex]() 148 | 149 | adjacencyLists.forEach { list in 150 | vertices += [list.vertex] 151 | } 152 | return vertices 153 | } 154 | 155 | var edges: [Edge] { 156 | var edges = Set>() 157 | 158 | adjacencyLists.forEach { list in 159 | list.edges.forEach { edge in 160 | edges.insert(edge) 161 | } 162 | } 163 | return Array(edges) 164 | } 165 | 166 | // MARK: - Initializers 167 | 168 | init() { 169 | // Default initializer 170 | // It is overrides so we don't get the initializer that is automatically synthesyzed for us by the compiler 171 | } 172 | 173 | // MARK: - Methods 174 | 175 | mutating func addVertex(basedOn data: T) -> Vertex { 176 | for list in adjacencyLists where list.vertex.data 177 | == data { 178 | return list.vertex 179 | } 180 | 181 | let vertex = Vertex(data: data, index: adjacencyLists.count) 182 | let adjacencyList = AdjacencyList(vertex: vertex) 183 | adjacencyLists += [adjacencyList] 184 | return vertex 185 | } 186 | 187 | mutating func addEdge(from: Vertex, to: Vertex, weight: Double? = nil) -> Edge { 188 | let edge = Edge(from: from, to: to, weight: weight) 189 | let list = adjacencyLists[from.index] 190 | 191 | if !list.edges.isEmpty { 192 | for listEdge in list.edges where listEdge == edge { 193 | return listEdge 194 | } 195 | adjacencyLists[from.index].edges += [edge] 196 | } else { 197 | adjacencyLists[from.index].edges = [edge] 198 | } 199 | return edge 200 | } 201 | 202 | func searchVertex(with data: T) -> [Vertex] { 203 | return vertices.filter { $0.data == data } 204 | } 205 | 206 | func searchEdges(with weight: Double) -> [Edge] { 207 | return edges.filter { $0.weight ?? 0 == weight } 208 | } 209 | 210 | func search(edge: Edge) -> [Edge] { 211 | return edges.filter { $0 == edge } 212 | } 213 | 214 | 215 | } 216 | 217 | 218 | //: Usage: 219 | 220 | var adjacencyListGraph = AdjacencyListGraph() 221 | let vertexNewYork = adjacencyListGraph.addVertex(basedOn: "New York") 222 | let vertexSanFrancisco = adjacencyListGraph.addVertex(basedOn: "San Francisco") 223 | let vertexMoscow = adjacencyListGraph.addVertex(basedOn: "Moscow") 224 | let vertexTokyo = adjacencyListGraph.addVertex(basedOn: "Tokyo") 225 | let vertexSidney = adjacencyListGraph.addVertex(basedOn: "Sidney") 226 | 227 | let edgeNYSF = adjacencyListGraph.addEdge(from: vertexNewYork, to: vertexSanFrancisco) 228 | let edgeSFMSC = adjacencyListGraph.addEdge(from: vertexSanFrancisco, to: vertexMoscow) 229 | let edgeMSCTKY = adjacencyListGraph.addEdge(from: vertexMoscow, to: vertexTokyo) 230 | let edgeTKYSDY = adjacencyListGraph.addEdge(from: vertexTokyo, to: vertexSidney) 231 | let edgeSDYMSC = adjacencyListGraph.addEdge(from: vertexSidney, to: vertexMoscow) 232 | 233 | 234 | print(adjacencyListGraph) 235 | 236 | 237 | let results = adjacencyListGraph.searchVertex(with: "Moscow") 238 | 239 | print("\nSearch Results: ") 240 | print(results) 241 | 242 | // Pleas note that the implementation is not complete. MST, Prim's and Dijkstra's algorithms will be added later. 243 | 244 | //: [Next](@next) 245 | -------------------------------------------------------------------------------- /Data Structures.playground/Sources/Heap.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public struct Heap where T: Comparable { 4 | 5 | // MARK: - Constants 6 | 7 | private let zero = 0 8 | 9 | // MARK: - Properties 10 | 11 | var nodes = [T]() 12 | 13 | public var isEmpty: Bool { 14 | return nodes.isEmpty 15 | } 16 | 17 | public var count: Int { 18 | return nodes.count 19 | } 20 | 21 | // MARK: - Private properties 22 | 23 | fileprivate var order: (T, T) -> Bool 24 | 25 | // MARK: - Initializers 26 | 27 | public init(order: @escaping (T, T) -> Bool) { 28 | self.order = order 29 | } 30 | 31 | public init(array: [T], order: @escaping (T, T) -> Bool) { 32 | self.order = order 33 | setup(using: array) 34 | } 35 | 36 | // MARK: - Methods 37 | 38 | public func peek() -> T? { 39 | return nodes.first 40 | } 41 | 42 | public mutating func insert(node: T) { 43 | nodes.append(node) 44 | shiftUp(count - 1) 45 | } 46 | 47 | public mutating func insert(_ sequence: S) where S.Iterator.Element == T { 48 | sequence.forEach { element in 49 | insert(node: element) 50 | } 51 | } 52 | 53 | public mutating func replace(elementAt index: Int, with value: T) { 54 | guard index < nodes.count else { return } 55 | remove(at: index) 56 | insert(node: value) 57 | } 58 | 59 | @discardableResult public mutating func remove() -> T? { 60 | guard !nodes.isEmpty else { return nil } 61 | 62 | switch nodes.count { 63 | case 1: 64 | return nodes.removeLast() 65 | default: 66 | let value = nodes[zero] 67 | nodes[zero] = nodes.removeLast() 68 | shiftDown(zero) 69 | return value 70 | } 71 | } 72 | 73 | @discardableResult public mutating func remove(at index: Int) -> T? { 74 | guard index < nodes.count else { return nil } 75 | let size = count - 1 76 | 77 | if index != size { 78 | nodes.swapAt(index, size) 79 | shiftDown(from: index, until: size) 80 | shiftUp(index) 81 | } 82 | return nodes.removeLast() 83 | } 84 | 85 | // MARK: - Shifting 86 | 87 | internal mutating func shiftDown(from startIndex: Int, until endIndex: Int) { 88 | let leftChildIndex = self.leftChildIndex(of: startIndex) 89 | let rightChildIndex = leftChildIndex + 1 90 | var first = startIndex 91 | 92 | if leftChildIndex < endIndex && order(nodes[leftChildIndex], nodes[first]) { 93 | first = leftChildIndex 94 | } 95 | if rightChildIndex < endIndex && order(nodes[rightChildIndex], nodes[first]) { 96 | first = rightChildIndex 97 | } 98 | if first == startIndex { return } 99 | 100 | nodes.swapAt(startIndex, first) 101 | shiftDown(from: first, until: endIndex) 102 | } 103 | 104 | internal mutating func shiftDown(_ index: Int) { 105 | shiftDown(from: index, until: count) 106 | } 107 | 108 | internal mutating func shiftUp(_ index: Int) { 109 | var childIndex = index 110 | let child = nodes[childIndex] 111 | var parentIndex = self.parentIndex(of: childIndex) 112 | 113 | while childIndex > 0 && order(child, nodes[parentIndex]) { 114 | nodes[childIndex] = nodes[parentIndex] 115 | childIndex = parentIndex 116 | parentIndex = self.parentIndex(of: childIndex) 117 | } 118 | nodes[childIndex] = child 119 | } 120 | 121 | // MARK: - Child indicies 122 | 123 | @inline(__always) internal func rightChildIndex(of index: Int) -> Int { 124 | return 2 * index + 2 125 | } 126 | 127 | @inline(__always) internal func leftChildIndex(of index: Int) -> Int { 128 | return 2 * index + 1 129 | } 130 | 131 | @inline(__always) internal func parentIndex(of index: Int) -> Int { 132 | return (index - 1) / 2 133 | } 134 | 135 | // MARK: - Private helpers 136 | 137 | private mutating func setup(using nodesArray: [T]) { 138 | self.nodes = nodesArray 139 | let strideCondition = stride(from: (count / 2 - 1), through: 0, by: -1) 140 | 141 | for index in strideCondition { 142 | shiftDown(index) 143 | } 144 | } 145 | 146 | } 147 | 148 | /*: 149 | Basically we created wrapper around standard Array type. However we need to carefully take a look at the two important pieces of the implenentation: 150 | - T type which is a generic placeholder 151 | - 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. 152 | 153 | 154 | The following extension adds support for debugging capabilites for both regular "print" and "debugPrint" statements 155 | */ 156 | extension Heap: CustomStringConvertible, CustomDebugStringConvertible { 157 | 158 | // MARK: - Properties 159 | 160 | public var description: String { 161 | return nodes.description 162 | } 163 | 164 | public var debugDescription: String { 165 | return prettyPrint() 166 | } 167 | 168 | // MARK: - Helpers 169 | 170 | private func prettyPrint() -> String { 171 | var out = String() 172 | var iterator = 2 173 | 174 | var copy = Array(nodes) 175 | var counter = copy.count 176 | 177 | while counter > 0, copy.count > 0 { 178 | if counter == nodes.count { 179 | // remove the first element only 180 | out += "{ \(copy.removeFirst()) " 181 | counter -= 1 182 | 183 | } else { 184 | out += "{ " 185 | var temp = iterator 186 | 187 | while temp > 0, copy.count > 0 { 188 | out += "\(copy.removeFirst()) " 189 | temp -= 1 190 | } 191 | 192 | counter -= iterator 193 | iterator = iterator * 2 194 | } 195 | out += "} \n" 196 | } 197 | 198 | return out 199 | } 200 | 201 | } 202 | 203 | extension Heap: ExpressibleByArrayLiteral { 204 | 205 | public init(arrayLiteral elements: T...) { 206 | self.init(array: elements, order: >) 207 | } 208 | } 209 | 210 | /*: 211 | 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. 212 | */ 213 | extension Heap: Sequence { 214 | 215 | public func makeIterator() -> AnyIterator { 216 | let idexedIterator = IndexingIterator(_elements: self.nodes.lazy.reversed()) 217 | return AnyIterator(idexedIterator) 218 | } 219 | } 220 | 221 | 222 | 223 | // MARK: - Heap Sort. Should be decomposed into separete file for sorting algorithms. 224 | // - Note that it is very similar to Selection sort approach. 225 | // - 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. 226 | extension Heap { 227 | func sorted() -> [T] { 228 | var heap = Heap(array: nodes, order: order) 229 | 230 | for index in self.nodes.indices.reversed() { 231 | heap.nodes.swapAt(0, index) 232 | heap.shiftDown(from: 0, until: index) 233 | } 234 | return heap.nodes 235 | } 236 | } 237 | 238 | 239 | // MARK: - Searching 240 | extension Heap where T: Equatable { 241 | 242 | /// Gets the index of a node from the Heap 243 | /// 244 | /// - Parameter node: is a Node of type T that is going to be searched 245 | /// - Returns: is an index in Heap array 246 | public func index(of node: T) -> Int? { 247 | return nodes.firstIndex(where: { $0 == node }) 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /Data Structures.playground/Pages/Red-Black Tree.xcplaygroundpage/Content.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | class RedBlackTreeNode { 4 | var value: T 5 | var isRed: Bool 6 | var left: RedBlackTreeNode? 7 | var right: RedBlackTreeNode? 8 | 9 | init(value: T, isRed: Bool, left: RedBlackTreeNode?, right: RedBlackTreeNode?) { 10 | self.value = value 11 | self.isRed = isRed 12 | self.left = left 13 | self.right = right 14 | } 15 | 16 | static func isRedNode(node: RedBlackTreeNode?) -> Bool { 17 | if (node == nil) { 18 | return false 19 | } 20 | return node!.isRed 21 | } 22 | 23 | func clockwiseRotate() -> RedBlackTreeNode { 24 | let t = self.left! 25 | self.left = t.right 26 | t.right = self 27 | t.isRed = RedBlackTreeNode.isRedNode(node: t.right) 28 | t.right!.isRed = true 29 | return t 30 | } 31 | 32 | func counterclockwiseRotate() -> RedBlackTreeNode { 33 | let t = self.right! 34 | self.right = t.left 35 | t.left = self 36 | t.isRed = RedBlackTreeNode.isRedNode(node: t.left) 37 | t.left!.isRed = true 38 | return t 39 | } 40 | 41 | func flip() { 42 | self.isRed = !self.isRed 43 | } 44 | 45 | func exchange() { 46 | self.flip() 47 | self.left?.flip() 48 | self.right?.flip() 49 | } 50 | 51 | func minimum() -> RedBlackTreeNode { 52 | if (self.left == nil) { 53 | return self 54 | } 55 | return self.left!.minimum() 56 | } 57 | 58 | func maximum() -> RedBlackTreeNode { 59 | if (self.right == nil) { 60 | return self 61 | } 62 | return self.right!.maximum() 63 | } 64 | 65 | func bubbleUp() -> RedBlackTreeNode { 66 | var t = self 67 | if (RedBlackTreeNode.isRedNode(node: t.right)) { 68 | t = t.counterclockwiseRotate() 69 | } 70 | if (RedBlackTreeNode.isRedNode(node: t.left) && RedBlackTreeNode.isRedNode(node: t.left!.left)) { 71 | t = t.clockwiseRotate() 72 | } 73 | if (RedBlackTreeNode.isRedNode(node: t.left) && RedBlackTreeNode.isRedNode(node: t.right)) { 74 | t.exchange() 75 | } 76 | return t 77 | } 78 | 79 | func alignLeftNode() -> RedBlackTreeNode { 80 | var t = self 81 | t.exchange() 82 | if (self.right != nil && RedBlackTreeNode.isRedNode(node: t.right!.left)) { 83 | t.right = t.right!.clockwiseRotate() 84 | t = t.counterclockwiseRotate() 85 | t.exchange() 86 | } 87 | return t 88 | } 89 | 90 | func alignRightNode() -> RedBlackTreeNode { 91 | var t = self 92 | t.exchange() 93 | if (self.left != nil && RedBlackTreeNode.isRedNode(node: t.left!.left)) { 94 | t = t.clockwiseRotate() 95 | t.exchange() 96 | } 97 | return t 98 | } 99 | 100 | func search(value: T) -> RedBlackTreeNode? { 101 | if (self.value == value) { 102 | return self 103 | } else if (self.value < value) { 104 | return self.left?.search(value: value) 105 | } else { 106 | return self.right?.search(value: value) 107 | } 108 | } 109 | 110 | func removeMinimum() -> RedBlackTreeNode? { 111 | if (self.left == nil) { 112 | return nil 113 | } 114 | var t = self 115 | if (!RedBlackTreeNode.isRedNode(node: t.left) && !RedBlackTreeNode.isRedNode(node: t.left!.left)) { 116 | t = t.alignLeftNode() 117 | } 118 | t.left = t.left!.removeMinimum() 119 | return t.bubbleUp() 120 | } 121 | 122 | func preorderTraversal() -> Array { 123 | var traversal: Array = [self.value] 124 | if (self.left != nil) { 125 | traversal += left!.inorderTraversal() 126 | } 127 | if (self.right != nil) { 128 | traversal += right!.inorderTraversal() 129 | } 130 | return traversal; 131 | } 132 | 133 | func inorderTraversal() -> Array { 134 | var traversal: Array = [] 135 | if (self.left != nil) { 136 | traversal += left!.inorderTraversal() 137 | } 138 | traversal += [self.value] 139 | if (self.right != nil) { 140 | traversal += right!.inorderTraversal() 141 | } 142 | return traversal; 143 | } 144 | 145 | func postorderTraversal() -> Array { 146 | var traversal: Array = [] 147 | if (self.left != nil) { 148 | traversal += left!.inorderTraversal() 149 | } 150 | if (self.right != nil) { 151 | traversal += right!.inorderTraversal() 152 | } 153 | traversal += [self.value] 154 | return traversal; 155 | } 156 | } 157 | 158 | class RedBlackTree { 159 | var root: RedBlackTreeNode? = nil 160 | 161 | static func insert(inRoot: RedBlackTreeNode?, val: T) -> RedBlackTreeNode? { 162 | var root = inRoot 163 | if (root == nil) { 164 | return RedBlackTreeNode(value: val, isRed: true, left: nil, right: nil) 165 | } 166 | if (RedBlackTreeNode.isRedNode(node: root!.left) && RedBlackTreeNode.isRedNode(node: root!.right)) { 167 | root!.exchange() 168 | } 169 | if (val == root!.value) { 170 | root!.value = val 171 | } else if (val < root!.value) { 172 | root!.left = insert(inRoot: root!.left, val: val) 173 | } else { 174 | root!.right = insert(inRoot: root!.right, val: val) 175 | } 176 | if (RedBlackTreeNode.isRedNode(node: root!.right)) { 177 | root = root!.counterclockwiseRotate() 178 | } 179 | if (RedBlackTreeNode.isRedNode(node: root!.left) && RedBlackTreeNode.isRedNode(node: root!.left!.left)) { 180 | root = root!.clockwiseRotate() 181 | } 182 | if (RedBlackTreeNode.isRedNode(node: root!.left) && RedBlackTreeNode.isRedNode(node: root!.right)) { 183 | root!.exchange(); 184 | } 185 | return root; 186 | } 187 | 188 | static func remove(inRoot: RedBlackTreeNode?, val: T) -> RedBlackTreeNode? { 189 | var root = inRoot; 190 | if (val < root!.value) { 191 | if (root!.left != nil && !(RedBlackTreeNode.isRedNode(node: root!.left)) && !(RedBlackTreeNode.isRedNode(node: root!.left!.left))) { 192 | root = root!.alignLeftNode() 193 | } 194 | root!.left = remove(inRoot: root!.left, val: val); 195 | } else { 196 | if (RedBlackTreeNode.isRedNode(node: root!.left)) { 197 | root = root!.clockwiseRotate() 198 | } 199 | if (val == root!.value && root!.right == nil) { 200 | return nil 201 | } 202 | if (root!.right != nil && !(RedBlackTreeNode.isRedNode(node: root!.right)) && !(RedBlackTreeNode.isRedNode(node: root!.right!.left))) { 203 | root = root!.alignRightNode() 204 | } 205 | if (val == root!.value) { 206 | root!.value = root!.right!.minimum().value 207 | root!.right = root!.right!.removeMinimum() 208 | } else { 209 | root!.right = remove(inRoot: root!.right, val: val); 210 | } 211 | } 212 | return root!.bubbleUp() 213 | } 214 | 215 | func insert(value: T) { 216 | root = RedBlackTree.insert(inRoot: root, val: value) 217 | if (root != nil) { 218 | root!.isRed = false 219 | } 220 | } 221 | 222 | func remove(value: T) { 223 | if (search(value: value) == nil) { 224 | return 225 | } 226 | root = RedBlackTree.remove(inRoot: root, val: value) 227 | if (root != nil) { 228 | root!.isRed = false 229 | } 230 | } 231 | 232 | func search(value: T) -> RedBlackTreeNode? { 233 | return root?.search(value: value) 234 | } 235 | 236 | func preorderTraversal() -> Array { 237 | if (root == nil) { 238 | return Array() 239 | } 240 | return root!.preorderTraversal() 241 | } 242 | 243 | func inorderTraversal() -> Array { 244 | if (root == nil) { 245 | return Array() 246 | } 247 | return root!.inorderTraversal() 248 | } 249 | 250 | func postorderTraversal() -> Array { 251 | if (root == nil) { 252 | return Array() 253 | } 254 | return root!.postorderTraversal() 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /Data Structures.playground/Pages/Queue.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: [Previous](@previous) 2 | 3 | import Foundation 4 | 5 | public struct Queue { 6 | 7 | // MARK: - Private properties 8 | 9 | private var data: [T] 10 | 11 | // MARK: - Public properties 12 | 13 | public var count: Int { 14 | return data.count 15 | } 16 | 17 | public var capacity: Int { 18 | get { 19 | return data.capacity 20 | } 21 | set { 22 | data.reserveCapacity(capacity) 23 | } 24 | } 25 | 26 | // MARK: - Initializers 27 | 28 | public init() { 29 | data = [T]() 30 | } 31 | 32 | public init(_ elements: S) where S.Iterator.Element == T { 33 | self.init() 34 | data.append(contentsOf: elements) 35 | } 36 | 37 | 38 | // MARK: - API methods 39 | 40 | public mutating func dequeue() -> T? { 41 | return data.removeFirst() 42 | } 43 | 44 | public func peek() -> T? { 45 | return data.first 46 | } 47 | 48 | public mutating func enqueue(element: T) { 49 | data.append(element) 50 | } 51 | 52 | public mutating func clear() { 53 | data.removeAll() 54 | } 55 | 56 | public func isFull() -> Bool { 57 | return count == data.capacity 58 | } 59 | 60 | public func isEmpty() -> Bool { 61 | return data.isEmpty 62 | } 63 | 64 | // MARK: - Private methods 65 | 66 | private func checkIndex(index: Int) throws { 67 | if index < 0 || index > count { 68 | throw QueueError.indexOutOfRange 69 | } 70 | } 71 | 72 | } 73 | 74 | // MARK: - Custom error enum type declaration 75 | 76 | enum QueueError: Error { 77 | case indexOutOfRange 78 | } 79 | 80 | // MARK: - CustomStringConvertable and CustomStirngDebugConvertable protocols conformance 81 | 82 | extension Queue: CustomStringConvertible, CustomDebugStringConvertible { 83 | 84 | public var description: String { 85 | return data.description 86 | } 87 | 88 | public var debugDescription: String { 89 | return data.description 90 | } 91 | } 92 | 93 | // MARK: - ExpressibleByArrayLiteral protocol conformance 94 | 95 | extension Queue: ExpressibleByArrayLiteral { 96 | 97 | public init(arrayLiteral elements: T...) { 98 | self.init(elements) 99 | } 100 | } 101 | 102 | // MARK: - Sequnce protocol conformance 103 | 104 | extension Queue: Sequence { 105 | 106 | public func makeIterator() -> AnyIterator { 107 | let indexedIterator = IndexingIterator(_elements: data.lazy) 108 | return AnyIterator(indexedIterator) 109 | } 110 | 111 | public func generate() -> AnyIterator { 112 | let indexingIteratoer = IndexingIterator(_elements: data.lazy) 113 | return AnyIterator(indexingIteratoer) 114 | 115 | } 116 | } 117 | 118 | // MARK: - MutableCollection protocol conformance 119 | 120 | extension Queue: MutableCollection { 121 | 122 | // MARK: - Core protocol conformance 123 | 124 | public var startIndex: Int { 125 | return 0 126 | } 127 | 128 | public var endIndex: Int { 129 | return count - 1 130 | } 131 | 132 | public func index(after i: Int) -> Int { 133 | return data.index(after: i) 134 | } 135 | 136 | // MARK: - Subscript implementation 137 | 138 | public subscript(index: Int) -> T { 139 | get { 140 | checkHandledIndex(index: index) 141 | return data[index] 142 | } 143 | set { 144 | checkHandledIndex(index: index) 145 | data[index] = newValue 146 | } 147 | } 148 | 149 | // MARK: - Utility method 150 | 151 | private func checkHandledIndex(index: Int) { 152 | do { 153 | try checkIndex(index: index) 154 | } catch { 155 | fatalError(error.localizedDescription) 156 | } 157 | } 158 | 159 | } 160 | 161 | 162 | //: The next step is to implement interactive scene for the data structure: 163 | 164 | import SpriteKit 165 | import PlaygroundSupport 166 | 167 | class QueueScene: SKScene { 168 | 169 | var queueElements: Queue! { 170 | didSet { 171 | if queueElements != nil { 172 | numberOfElements.text = "\(queueElements.count) animals" 173 | } 174 | } 175 | } 176 | lazy var numberOfElements: SKLabelNode = { 177 | let label = SKLabelNode(text: "0 animals") 178 | label.position = CGPoint(x: 500, y: 300) 179 | label.color = .gray 180 | label.fontSize = 28 181 | label.verticalAlignmentMode = .center 182 | return label 183 | }() 184 | 185 | override func didMove(to view: SKView) { 186 | queueElements = createRandomQeueueElementNodeArray(for: 4) 187 | appearenceAnimation() 188 | 189 | dequeueButton() 190 | enqueueButton() 191 | label() 192 | description() 193 | 194 | addChild(numberOfElements) 195 | } 196 | 197 | func label() { 198 | let nameLabel = SKLabelNode(text: "Queue") 199 | nameLabel.position = CGPoint(x: 300, y: 760) 200 | nameLabel.color = .gray 201 | nameLabel.fontSize = 64 202 | nameLabel.verticalAlignmentMode = .center 203 | addChild(nameLabel) 204 | } 205 | 206 | func description() { 207 | let nameLabel = SKLabelNode(text: "Limit is 7 animals") 208 | nameLabel.position = CGPoint(x: 300, y: 700) 209 | nameLabel.color = .darkGray 210 | nameLabel.fontSize = 24 211 | nameLabel.verticalAlignmentMode = .center 212 | addChild(nameLabel) 213 | } 214 | 215 | func dequeueButton() { 216 | let popButton = SKSpriteNode(color: .white, size: CGSize(width: 120, height: 50)) 217 | popButton.position = CGPoint(x: 100, y: 700) 218 | popButton.name = "pop" 219 | 220 | let popLabel = SKLabelNode(text: "Dequeue") 221 | popLabel.fontColor = SKColor.darkGray 222 | popLabel.fontSize = 24 223 | 224 | popLabel.verticalAlignmentMode = SKLabelVerticalAlignmentMode.center 225 | 226 | popButton.addChild(popLabel) 227 | addChild(popButton) 228 | } 229 | 230 | func enqueueButton() { 231 | let pushButton = SKSpriteNode(color: .white, size: CGSize(width: 120, height: 50)) 232 | pushButton.position = CGPoint(x: 500, y: 700) 233 | pushButton.name = "push" 234 | 235 | let pushLabel = SKLabelNode(text: "Enqueue") 236 | pushLabel.fontColor = SKColor.darkGray 237 | pushLabel.fontSize = 24 238 | pushLabel.verticalAlignmentMode = SKLabelVerticalAlignmentMode.center 239 | 240 | pushButton.addChild(pushLabel) 241 | addChild(pushButton) 242 | } 243 | 244 | func appearenceAnimation() { 245 | 246 | // Animate creation of the stack of books 247 | 248 | let appearAction = SKAction.unhide() 249 | 250 | for (index, book) in queueElements.enumerated() { 251 | book.position = CGPoint.init(x: 300, y: 500) 252 | book.isHidden = true 253 | addChild(book) 254 | 255 | let moveDown = SKAction.move(to: CGPoint(x: 300, y: CGFloat(80 * CGFloat(index + 1))), duration: 1.5) 256 | let waitAction = SKAction.wait(forDuration: TimeInterval(index * 2)) 257 | let sequence = SKAction.sequence([waitAction, appearAction, moveDown]) 258 | book.run(sequence) 259 | } 260 | } 261 | 262 | func createRandomQeueueElementNodeArray(for numberOfElements: Int) -> Queue { 263 | var nodes: Queue = Queue() 264 | 265 | for _ in 0.., with event: UIEvent?) { 275 | guard let selfLocation = touches.first?.location(in: self) else { 276 | return 277 | } 278 | let nodes = self.nodes(at: selfLocation) 279 | 280 | for node in nodes { 281 | if node.name == "pop" { 282 | // pop action 283 | let element = queueElements.dequeue() 284 | let moveUpAction = SKAction.move(by: CGVector(dx: 0, dy: -200), duration: 0.5) 285 | let removeAction = SKAction.removeFromParent() 286 | let moveQueueDown = SKAction.run { 287 | let moveDownAction = SKAction.move(by: CGVector(dx: 0, dy: -80), duration: 0.5) 288 | self.queueElements.forEach({ (node) in 289 | node.run(moveDownAction) 290 | }) 291 | } 292 | let actionSequence = SKAction.sequence([moveUpAction, removeAction, moveQueueDown]) 293 | element?.run(actionSequence) 294 | } else if node.name == "push", queueElements.count < 7 { 295 | let node = QueueElementNode() 296 | node.position = CGPoint.init(x: 300, y: 600) 297 | node.isHidden = true 298 | addChild(node) 299 | queueElements.enqueue(element: node) 300 | 301 | let appearAction = SKAction.unhide() 302 | let moveDown = SKAction.move(to: CGPoint(x: 300, y: CGFloat(80 * CGFloat(queueElements.count))), duration: 1.5) 303 | let waitAction = SKAction.wait(forDuration: TimeInterval(1)) 304 | let sequence = SKAction.sequence([waitAction, appearAction, moveDown]) 305 | node.run(sequence) 306 | } 307 | 308 | } 309 | } 310 | 311 | } 312 | 313 | let skScene = QueueScene(size: CGSize(width: 600, height: 800)) 314 | skScene.backgroundColor = .black 315 | 316 | 317 | let skView = SKView(frame: CGRect(x: 0, y: 0, width: 600, height: 800)) 318 | skView.presentScene(skScene) 319 | PlaygroundPage.current.liveView = skView 320 | 321 | 322 | //: [Next](@next) 323 | -------------------------------------------------------------------------------- /Data Structures.playground/Pages/Stack.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: [Previous](@previous) 2 | 3 | /*: 4 | Stack is a common data structure in Comouter Science. Conceptually the data strcuture works using Last In First Out (LIFO) principle. Actually you already know how this principle works even if these are the first steps in programming. LIFO principle can be easily expressed as a stack of books: the last book added to the stack is the first one removed. Of cource, in real life you can remove any book that you want, we have more freedom to act, but imagine that books are stored in a container. That contaier physically restricts you from removing the last book, so you are forced to remove the top one in order to get to the last one. As I said - you already know what LIFO and Stack is. 5 | 6 | That introduction was mostly conceptual. Now lets dive into technical details and think about how we can implement such behaviour. First of all lets decide if Stack should be implemented as a class or a struct. Since structures in Swift are far more powerful than in other C-based programming languages and you can use the same protocol-oriented approach as you can with classes, it is a good idea to use structures over classes. Structures will allow us to create simple building blocks that are restricted from the box - meaning that you do not need to worry about inheritence, deinitialization and reference management. 7 | 8 | Lets think about the minimum required interface for our Stack data structure: 9 | 10 | Methods: 11 | - push() - adds a new element to the top of the stack 12 | - pop() - removes and returns an element from the top of the stack 13 | - peek() - returns an elemtn form the top, but does not remove it 14 | 15 | Properties: 16 | - isEmpty - returns a boolean indicator that tells if stack is empty or not 17 | - count - contains the current number of elemtns in stack 18 | 19 | Stack can be implemented by using Array or Linked List data structures; each one has its pros and cons depending on the perfomance characteristics required. 20 | */ 21 | 22 | import UIKit 23 | 24 | public struct Stack { 25 | 26 | // MARK: - Private properties 27 | 28 | private var elements: [T] 29 | 30 | // MARK: - Public computed properties 31 | 32 | public var isEmpty: Bool { 33 | return elements.isEmpty 34 | } 35 | 36 | public var count: Int { 37 | return elements.count 38 | } 39 | 40 | // MARK: - Initializers 41 | 42 | public init() { 43 | elements = [T]() 44 | } 45 | 46 | public init(stack: S) where S.Iterator.Element == T { 47 | self.elements = Array(stack.reversed()) 48 | } 49 | 50 | // MARK: Methods 51 | 52 | public mutating func push(element: T) { 53 | elements.append(element) 54 | } 55 | 56 | public mutating func peek() -> T? { 57 | return elements.last 58 | } 59 | 60 | public mutating func pop() -> T? { 61 | return elements.popLast() 62 | } 63 | 64 | } 65 | 66 | /*: 67 | Basically we created wrapper around standard Array type. However we need to carefully take a look at the two important pieces of the implenentation: 68 | - T type which is a generic placeholder 69 | - 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. 70 | 71 | 72 | The following extension adds support for debugging capabilites for both regular "print" and "debugPrint" statements 73 | */ 74 | 75 | extension Stack: CustomStringConvertible, CustomDebugStringConvertible { 76 | 77 | public var description: String { 78 | return elements.description 79 | } 80 | 81 | public var debugDescription: String { 82 | return elements.description 83 | } 84 | } 85 | 86 | /*: 87 | Adds support for new behaviour when Stack can be initialized by an array literal e.g. [1,2,3,4] for appropriate cases. Again, this is for convenince and compatability with the standard library and other data structures. 88 | */ 89 | extension Stack: ExpressibleByArrayLiteral { 90 | 91 | public init(arrayLiteral elements: T...) { 92 | self.init() 93 | 94 | elements.forEach { element in 95 | self.elements.append(element) 96 | } 97 | } 98 | } 99 | 100 | /*: 101 | 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. 102 | */ 103 | extension Stack: Sequence { 104 | 105 | public func makeIterator() -> AnyIterator { 106 | let idexedIterator = IndexingIterator(_elements: self.elements.lazy.reversed()) 107 | return AnyIterator(idexedIterator) 108 | } 109 | } 110 | 111 | 112 | /*: 113 | Now lets create a sample Stack from an array literal and play around with the implementation: 114 | */ 115 | 116 | 117 | var stack: Stack = [1,2,3,4,5,6,7,8,1] 118 | 119 | stack.forEach { element in 120 | debugPrint("element: ", element) 121 | } 122 | 123 | // Explicitly cast to Any to silence compiler warning. 124 | let lastElement = stack.pop() as Any 125 | debugPrint("last removed element: ", lastElement) 126 | 127 | stack.push(element: 10) 128 | debugPrint("pushed 10: ", stack) 129 | 130 | /*: 131 | Great! Everything works as expected. Now time to move on and implement visualization using SpriteKit framework. Thankfully our Stack is generic which means that we can easily create some custom SKSpriteNode classes and visualize the data structure. 132 | */ 133 | 134 | import SpriteKit 135 | import PlaygroundSupport 136 | 137 | class StackScene: SKScene { 138 | 139 | var stackElements: Stack! { 140 | didSet { 141 | if stackElements != nil { 142 | numberOfElements.text = "\(stackElements.count) books" 143 | } 144 | } 145 | } 146 | lazy var numberOfElements: SKLabelNode = { 147 | let label = SKLabelNode(text: "0 books") 148 | label.position = CGPoint(x: 500, y: 300) 149 | label.color = .gray 150 | label.fontSize = 28 151 | label.verticalAlignmentMode = .center 152 | return label 153 | }() 154 | 155 | override func didMove(to view: SKView) { 156 | stackElements = createRandomStackElementNodeArray(for: 4) 157 | appearenceAnimation() 158 | 159 | popButton() 160 | pushButton() 161 | label() 162 | description() 163 | 164 | addChild(numberOfElements) 165 | } 166 | 167 | func label() { 168 | let nameLabel = SKLabelNode(text: "Stack") 169 | nameLabel.position = CGPoint(x: 300, y: 760) 170 | nameLabel.color = .gray 171 | nameLabel.fontSize = 64 172 | nameLabel.verticalAlignmentMode = .center 173 | addChild(nameLabel) 174 | } 175 | 176 | func description() { 177 | let nameLabel = SKLabelNode(text: "Limit is 9 books") 178 | nameLabel.position = CGPoint(x: 300, y: 700) 179 | nameLabel.color = .darkGray 180 | nameLabel.fontSize = 24 181 | nameLabel.verticalAlignmentMode = .center 182 | addChild(nameLabel) 183 | } 184 | 185 | func popButton() { 186 | let popButton = SKSpriteNode(color: .white, size: CGSize(width: 120, height: 50)) 187 | popButton.position = CGPoint(x: 100, y: 700) 188 | popButton.name = "pop" 189 | 190 | let popLabel = SKLabelNode(text: "Pop") 191 | popLabel.fontColor = SKColor.darkGray 192 | popLabel.fontSize = 24 193 | 194 | popLabel.verticalAlignmentMode = SKLabelVerticalAlignmentMode.center 195 | 196 | popButton.addChild(popLabel) 197 | addChild(popButton) 198 | } 199 | 200 | func pushButton() { 201 | let pushButton = SKSpriteNode(color: .white, size: CGSize(width: 120, height: 50)) 202 | pushButton.position = CGPoint(x: 500, y: 700) 203 | pushButton.name = "push" 204 | 205 | let pushLabel = SKLabelNode(text: "Push") 206 | pushLabel.fontColor = SKColor.darkGray 207 | pushLabel.fontSize = 24 208 | pushLabel.verticalAlignmentMode = SKLabelVerticalAlignmentMode.center 209 | 210 | pushButton.addChild(pushLabel) 211 | addChild(pushButton) 212 | } 213 | 214 | func appearenceAnimation() { 215 | 216 | // Animate creation of the stack of books 217 | 218 | let appearAction = SKAction.unhide() 219 | 220 | for (index, book) in stackElements.reversed().enumerated() { 221 | book.position = CGPoint.init(x: 300, y: 500) 222 | book.isHidden = true 223 | addChild(book) 224 | 225 | let moveDown = SKAction.move(to: CGPoint(x: 300, y: CGFloat(50 * CGFloat(index + 1))), duration: 1.5) 226 | let waitAction = SKAction.wait(forDuration: TimeInterval(index * 2)) 227 | let sequence = SKAction.sequence([waitAction, appearAction, moveDown]) 228 | book.run(sequence) 229 | } 230 | } 231 | 232 | func createRandomStackElementNodeArray(for numberOfElements: Int) -> Stack { 233 | var nodes: Stack = Stack() 234 | 235 | for _ in 0.., with event: UIEvent?) { 245 | guard let selfLocation = touches.first?.location(in: self) else { 246 | return 247 | } 248 | let nodes = self.nodes(at: selfLocation) 249 | 250 | for node in nodes { 251 | if node.name == "pop" { 252 | // pop action 253 | let element = stackElements.pop() 254 | let moveUpAction = SKAction.move(by: CGVector(dx: 0, dy: 200), duration: 2.0) 255 | let fadeOut = SKAction.fadeOut(withDuration: 1.0) 256 | let removeAction = SKAction.removeFromParent() 257 | let actionSequence = SKAction.sequence([moveUpAction, fadeOut, removeAction]) 258 | element?.run(actionSequence) 259 | } else if node.name == "push", stackElements.count <= 8 { 260 | let node = StackElementNode() 261 | node.position = CGPoint.init(x: 300, y: 600) 262 | node.isHidden = true 263 | addChild(node) 264 | stackElements.push(element: node) 265 | 266 | let appearAction = SKAction.unhide() 267 | let moveDown = SKAction.move(to: CGPoint(x: 300, y: CGFloat(50 * CGFloat(stackElements.count))), duration: 1.5) 268 | let waitAction = SKAction.wait(forDuration: TimeInterval(1)) 269 | let sequence = SKAction.sequence([waitAction, appearAction, moveDown]) 270 | node.run(sequence) 271 | } 272 | 273 | } 274 | } 275 | 276 | } 277 | 278 | let skScene = StackScene(size: CGSize(width: 600, height: 800)) 279 | skScene.backgroundColor = .black 280 | 281 | 282 | let skView = SKView(frame: CGRect(x: 0, y: 0, width: 600, height: 800)) 283 | skView.presentScene(skScene) 284 | PlaygroundPage.current.liveView = skView 285 | 286 | //: [Next](@next) 287 | 288 | -------------------------------------------------------------------------------- /Data Structures.playground/Pages/BinarySearchTree.xcplaygroundpage/Contents.swift: -------------------------------------------------------------------------------- 1 | //: [Previous](@previous) 2 | 3 | import Foundation 4 | 5 | /// Time complexity for the Binary earch tree is O(log(n)) where n is the depth of the tree. In cases when binary seach trees are built suing linked lists, time complexity increases to O(n) since more levels need to be traveresed in order to insert/delete/find elements. 6 | // The implementation is going to be built around reference type. We begin from declaring new class and four main properties: 7 | // - the value that the current node holds 8 | // - reference to the left child node 9 | // - reference to the right child node 10 | // - reference to the parent node 11 | // In order to prevent retain cycle we mark the parent node property as weak and we declare each node property as an optional. That is reuiqred so we will be able to freely choose where to add new nodes. 12 | 13 | enum BinarySearchTreeError: Error { 14 | case wrongMethodCall(message: String) 15 | } 16 | 17 | class BinarySearchTree { 18 | 19 | // MARK: - Properties 20 | 21 | var value: T 22 | var leftChild: BinarySearchTree? 23 | var rightChild: BinarySearchTree? 24 | weak var parent: BinarySearchTree? 25 | 26 | // MARK: - Initializers 27 | 28 | convenience init(value: T) { 29 | self.init(value: value, leftChild: nil, rightChild: nil, parent: nil) 30 | } 31 | 32 | init(value: T, leftChild: BinarySearchTree?, rightChild: BinarySearchTree?, parent: BinarySearchTree?) { 33 | self.value = value 34 | self.leftChild = leftChild 35 | self.rightChild = rightChild 36 | self.parent = parent 37 | } 38 | 39 | // MARK: - Methods 40 | 41 | func height() -> Int { 42 | if leftChild == nil, rightChild == nil { 43 | return 0 44 | } 45 | return 1 + max(leftChild?.height() ?? 0, rightChild?.height() ?? 0) 46 | } 47 | 48 | func depth() -> Int { 49 | guard var parentNode = parent else { 50 | return 0 51 | } 52 | 53 | var depth = 1 54 | while let grandParentNode = parentNode.parent { 55 | depth += 1 56 | parentNode = grandParentNode 57 | } 58 | return depth 59 | } 60 | 61 | /// Inorder binary tree traversal simply means a traversal with the following rule: left value < node value < right value 62 | /// As a result we get sorted results from the smallest value to the greathest (according to the comparator patter, since each element must conform to Comparable protocol) 63 | /// - node: is a BinarySearchTreeNode? which you would like to traverse 64 | /// - handler: is an escaping closure that accept T argument and returns void. Use this injectasble closure in order to get the values from the traversal, then accumulate them or process them in place. 65 | class func traverseInorder(from node: BinarySearchTree?, handler: @escaping (T)->()) { 66 | // Exit condition from the recursize method calls 67 | guard let node = node else { 68 | return 69 | } 70 | 71 | traverseInorder(from: node.leftChild, handler: handler) 72 | handler(node.value) 73 | traverseInorder(from: node.rightChild, handler: handler) 74 | } 75 | 76 | /// Preorder binary tree traversal simply means a traversal with the following rule: node value -> left value -> right value 77 | /// - node: is a BinarySearchTreeNode? which you would like to traverse 78 | /// - handler: is an escaping closure that accept T argument and returns void. Use this injectasble closure in order to get the values from the traversal, then accumulate them or process them in place. 79 | class func traversePreorder(from node: BinarySearchTree?, handler: @escaping (T)->()) { 80 | // Exit condition from the recursize method calls 81 | guard let node = node else { 82 | return 83 | } 84 | 85 | handler(node.value) 86 | traverseInorder(from: node.leftChild, handler: handler) 87 | traverseInorder(from: node.rightChild, handler: handler) 88 | } 89 | 90 | /// Preorder binary tree traversal simply means a traversal with the following rule: left value -> right value -> node value 91 | /// - node: is a BinarySearchTreeNode? which you would like to traverse 92 | /// - handler: is an escaping closure that accept T argument and returns void. Use this injectasble closure in order to get the values from the traversal, then accumulate them or process them in place. 93 | class func traversePostorder(from node: BinarySearchTree?, handler: @escaping (T)->()) { 94 | // Exit condition from the recursize method calls 95 | guard let node = node else { 96 | return 97 | } 98 | 99 | traverseInorder(from: node.leftChild, handler: handler) 100 | traverseInorder(from: node.rightChild, handler: handler) 101 | handler(node.value) 102 | } 103 | 104 | func search(value: T) -> BinarySearchTree? { 105 | if value == self.value { 106 | return self 107 | } 108 | 109 | if value < self.value { 110 | guard let left = leftChild else { 111 | return nil 112 | } 113 | return left.search(value: value) 114 | } else { 115 | guard let right = rightChild else { 116 | return nil 117 | } 118 | return right.search(value: value) 119 | } 120 | } 121 | 122 | func insertNode(for value: T) throws { 123 | // If root exists that means we are trying to incert new node not from the root of the tree, so the operation is aborted 124 | if let _ = self.parent { 125 | throw BinarySearchTreeError.wrongMethodCall(message: "The method must be called from the root node") 126 | } 127 | addNode(value: value) 128 | } 129 | 130 | func delete() { 131 | if let left = leftChild { 132 | if let _ = rightChild { 133 | // 2 children 134 | exchangeWithSuccessor(node: self) 135 | } else { 136 | // 1 child - left 137 | attachParent(to: left, for: self) 138 | } 139 | } else if let right = rightChild { 140 | // 1 child - right 141 | attachParent(to: right, for: self) 142 | } else { 143 | // connet the node to the parent node 144 | attachParent(to: nil, for: self) 145 | } 146 | parent = nil 147 | leftChild = nil 148 | rightChild = nil 149 | } 150 | 151 | func deleteNode(for value: T) -> Bool { 152 | guard let nodeToDelete = search(value: value) else { 153 | return false 154 | } 155 | 156 | if let left = nodeToDelete.leftChild { 157 | if let _ = nodeToDelete.rightChild { 158 | // 2 children 159 | exchangeWithSuccessor(node: nodeToDelete) 160 | } else { 161 | // 1 child - left 162 | attachParent(to: left, for: nodeToDelete) 163 | } 164 | } else if let right = nodeToDelete.rightChild { 165 | // 1 child - right 166 | attachParent(to: right, for: nodeToDelete) 167 | } else { 168 | // connet the node to the parent node 169 | attachParent(to: nil, for: nodeToDelete) 170 | } 171 | nodeToDelete.parent = nil 172 | nodeToDelete.leftChild = nil 173 | nodeToDelete.rightChild = nil 174 | 175 | return true 176 | } 177 | 178 | // MARK: - Private methods 179 | 180 | private func addNode(value: T) { 181 | if value < self.value { 182 | if let leftChild = self.leftChild { 183 | leftChild.addNode(value: value) 184 | } else { 185 | let newNode = createNewNode(with: value) 186 | newNode.parent = self 187 | leftChild = newNode 188 | } 189 | } else { 190 | if let rightChild = self.rightChild { 191 | rightChild.addNode(value: value) 192 | } else { 193 | let newNode = createNewNode(with: value) 194 | newNode.parent = self 195 | rightChild = newNode 196 | } 197 | } 198 | } 199 | 200 | private func createNewNode(with value: T) -> BinarySearchTree { 201 | let newNode = BinarySearchTree(value: value) 202 | return newNode 203 | } 204 | 205 | private func attachParent(to child: BinarySearchTree?, for node: BinarySearchTree) { 206 | guard let parent = node.parent else { 207 | child?.parent = node.parent 208 | return 209 | } 210 | if parent.leftChild === node { 211 | parent.leftChild = child 212 | child?.parent = parent 213 | } else if parent.rightChild === node { 214 | parent.rightChild = child 215 | child?.parent = parent 216 | } 217 | } 218 | 219 | private func exchangeWithSuccessor(node: BinarySearchTree) { 220 | guard let right = node.rightChild, let left = node.leftChild else { 221 | return 222 | } 223 | let successor = right.minNode() 224 | successor.delete() 225 | 226 | successor.leftChild = left 227 | left.parent = successor 228 | 229 | if right !== successor { 230 | successor.rightChild = right 231 | right.parent = successor 232 | } else { 233 | successor.rightChild = nil 234 | } 235 | attachParent(to: successor, for: node) 236 | } 237 | 238 | private func minValue() -> T { 239 | if let left = leftChild { 240 | return left.minValue() 241 | } else { 242 | return value 243 | } 244 | } 245 | 246 | private func maxValue() -> T { 247 | if let right = rightChild { 248 | return right.maxValue() 249 | } else { 250 | return value 251 | } 252 | } 253 | 254 | private func minNode() -> BinarySearchTree { 255 | if let left = leftChild { 256 | return left.minNode() 257 | } else { 258 | return self 259 | } 260 | } 261 | 262 | private func maxNode() -> BinarySearchTree { 263 | if let right = rightChild { 264 | return right.maxNode() 265 | } else { 266 | return self 267 | } 268 | } 269 | 270 | } 271 | 272 | extension BinarySearchTree: CustomStringConvertible, CustomDebugStringConvertible { 273 | 274 | // MARK: - Overriden properties 275 | 276 | var description: String { 277 | return prepareStringConvertable() 278 | } 279 | 280 | var debugDescription: String { 281 | return prepareStringConvertable() 282 | } 283 | 284 | // MARK: - Methods 285 | 286 | func prepareStringConvertable() -> String { 287 | var desc = """ 288 | value: \(value) 289 | """ 290 | 291 | if let leftChild = self.leftChild { 292 | desc += """ 293 | \t left: [\(leftChild.description)] 294 | 295 | """ 296 | } 297 | if let rightChild = self.rightChild { 298 | desc += """ 299 | \t right: [\(rightChild.description)] 300 | """ 301 | } 302 | 303 | return desc 304 | } 305 | } 306 | 307 | //: Usage: 308 | 309 | // Let's use Int as out type for the current tree and see what results it produces in practice: 310 | 311 | let rootNode = BinarySearchTree(value: 10) 312 | try! rootNode.insertNode(for: 20) 313 | try! rootNode.insertNode(for: 5) 314 | try! rootNode.insertNode(for: 21) 315 | try! rootNode.insertNode(for: 8) 316 | try! rootNode.insertNode(for: 16) 317 | try! rootNode.insertNode(for: 4) 318 | 319 | print(rootNode) 320 | 321 | let wasDeleted = rootNode.deleteNode(for: 5) 322 | print("wasDeleted ", wasDeleted) 323 | 324 | print(rootNode) 325 | 326 | var assembledElements = [Int]() 327 | BinarySearchTree.traverseInorder(from: rootNode, handler: { value in 328 | assembledElements += [value] 329 | }) 330 | print(assembledElements) 331 | 332 | var preorderElements = [Int]() 333 | BinarySearchTree.traversePreorder(from: rootNode, handler: { value in 334 | preorderElements += [value] 335 | }) 336 | print(preorderElements) 337 | 338 | var postorderElements = [Int]() 339 | BinarySearchTree.traversePostorder(from: rootNode, handler: { value in 340 | postorderElements += [value] 341 | }) 342 | print(postorderElements) 343 | 344 | // Search the number 345 | let node = rootNode.search(value: 5) 346 | print(node as Any) 347 | 348 | // Great! Now let's change the type to String and do the same: 349 | 350 | let rootNodeString = BinarySearchTree(value: "Hello") 351 | try! rootNodeString.insertNode(for: "World") 352 | try! rootNodeString.insertNode(for: "We") 353 | try! rootNodeString.insertNode(for: "Love") 354 | try! rootNodeString.insertNode(for: "Swift") 355 | try! rootNodeString.insertNode(for: "Programming") 356 | try! rootNodeString.insertNode(for: "Language") 357 | 358 | print(rootNodeString) 359 | 360 | var assembledElementsString = [String]() 361 | BinarySearchTree.traverseInorder(from: rootNodeString, handler: { value in 362 | assembledElementsString += [value] 363 | }) 364 | print(assembledElementsString) 365 | 366 | //: [Next](@next) 367 | --------------------------------------------------------------------------------