├── _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 [](https://github.com/sindresorhus/awesome)
2 |
3 | []()
4 | []()
5 |
6 | **Last Update: 18/July/2021.**
7 |
8 | 
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 |
--------------------------------------------------------------------------------