diff --git a/3Sum and 4Sum/3Sum.playground/Contents.swift b/Algorithmic Problems/3Sum and 4Sum/3Sum.playground/Contents.swift similarity index 100% rename from 3Sum and 4Sum/3Sum.playground/Contents.swift rename to Algorithmic Problems/3Sum and 4Sum/3Sum.playground/Contents.swift diff --git a/3Sum and 4Sum/3Sum.playground/contents.xcplayground b/Algorithmic Problems/3Sum and 4Sum/3Sum.playground/contents.xcplayground similarity index 100% rename from 3Sum and 4Sum/3Sum.playground/contents.xcplayground rename to Algorithmic Problems/3Sum and 4Sum/3Sum.playground/contents.xcplayground diff --git a/3Sum and 4Sum/3Sum.playground/playground.xcworkspace/contents.xcworkspacedata b/Algorithmic Problems/3Sum and 4Sum/3Sum.playground/playground.xcworkspace/contents.xcworkspacedata similarity index 100% rename from 3Sum and 4Sum/3Sum.playground/playground.xcworkspace/contents.xcworkspacedata rename to Algorithmic Problems/3Sum and 4Sum/3Sum.playground/playground.xcworkspace/contents.xcworkspacedata diff --git a/3Sum and 4Sum/3Sum.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Algorithmic Problems/3Sum and 4Sum/3Sum.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from 3Sum and 4Sum/3Sum.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to Algorithmic Problems/3Sum and 4Sum/3Sum.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/3Sum and 4Sum/4Sum.playground/Contents.swift b/Algorithmic Problems/3Sum and 4Sum/4Sum.playground/Contents.swift similarity index 100% rename from 3Sum and 4Sum/4Sum.playground/Contents.swift rename to Algorithmic Problems/3Sum and 4Sum/4Sum.playground/Contents.swift diff --git a/3Sum and 4Sum/4Sum.playground/contents.xcplayground b/Algorithmic Problems/3Sum and 4Sum/4Sum.playground/contents.xcplayground similarity index 100% rename from 3Sum and 4Sum/4Sum.playground/contents.xcplayground rename to Algorithmic Problems/3Sum and 4Sum/4Sum.playground/contents.xcplayground diff --git a/3Sum and 4Sum/4Sum.playground/playground.xcworkspace/contents.xcworkspacedata b/Algorithmic Problems/3Sum and 4Sum/4Sum.playground/playground.xcworkspace/contents.xcworkspacedata similarity index 100% rename from 3Sum and 4Sum/4Sum.playground/playground.xcworkspace/contents.xcworkspacedata rename to Algorithmic Problems/3Sum and 4Sum/4Sum.playground/playground.xcworkspace/contents.xcworkspacedata diff --git a/3Sum and 4Sum/4Sum.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Algorithmic Problems/3Sum and 4Sum/4Sum.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from 3Sum and 4Sum/4Sum.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to Algorithmic Problems/3Sum and 4Sum/4Sum.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/3Sum and 4Sum/README.md b/Algorithmic Problems/3Sum and 4Sum/README.md similarity index 100% rename from 3Sum and 4Sum/README.md rename to Algorithmic Problems/3Sum and 4Sum/README.md diff --git a/All-Pairs Shortest Paths/APSP/APSP.playground/playground.xcworkspace/contents.xcworkspacedata b/Archived Code/3Sum.playground/playground.xcworkspace/contents.xcworkspacedata similarity index 100% rename from All-Pairs Shortest Paths/APSP/APSP.playground/playground.xcworkspace/contents.xcworkspacedata rename to Archived Code/3Sum.playground/playground.xcworkspace/contents.xcworkspacedata diff --git a/All-Pairs Shortest Paths/APSP/APSP.playground/Contents.swift b/Archived Code/APSP/APSP.playground/Contents.swift similarity index 100% rename from All-Pairs Shortest Paths/APSP/APSP.playground/Contents.swift rename to Archived Code/APSP/APSP.playground/Contents.swift diff --git a/All-Pairs Shortest Paths/APSP/APSP.playground/contents.xcplayground b/Archived Code/APSP/APSP.playground/contents.xcplayground similarity index 100% rename from All-Pairs Shortest Paths/APSP/APSP.playground/contents.xcplayground rename to Archived Code/APSP/APSP.playground/contents.xcplayground diff --git a/All-Pairs Shortest Paths/APSP/APSP.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Archived Code/APSP/APSP.playground/playground.xcworkspace/contents.xcworkspacedata similarity index 100% rename from All-Pairs Shortest Paths/APSP/APSP.xcodeproj/project.xcworkspace/contents.xcworkspacedata rename to Archived Code/APSP/APSP.playground/playground.xcworkspace/contents.xcworkspacedata diff --git a/All-Pairs Shortest Paths/APSP/APSP.playground/timeline.xctimeline b/Archived Code/APSP/APSP.playground/timeline.xctimeline similarity index 100% rename from All-Pairs Shortest Paths/APSP/APSP.playground/timeline.xctimeline rename to Archived Code/APSP/APSP.playground/timeline.xctimeline diff --git a/All-Pairs Shortest Paths/APSP/APSP.xcodeproj/project.pbxproj b/Archived Code/APSP/APSP.xcodeproj/project.pbxproj similarity index 100% rename from All-Pairs Shortest Paths/APSP/APSP.xcodeproj/project.pbxproj rename to Archived Code/APSP/APSP.xcodeproj/project.pbxproj diff --git a/Archived Code/APSP/APSP.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Archived Code/APSP/APSP.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/Archived Code/APSP/APSP.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/All-Pairs Shortest Paths/APSP/APSP.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/Archived Code/APSP/APSP.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from All-Pairs Shortest Paths/APSP/APSP.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to Archived Code/APSP/APSP.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/All-Pairs Shortest Paths/APSP/APSP.xcodeproj/xcshareddata/xcschemes/APSP.xcscheme b/Archived Code/APSP/APSP.xcodeproj/xcshareddata/xcschemes/APSP.xcscheme similarity index 100% rename from All-Pairs Shortest Paths/APSP/APSP.xcodeproj/xcshareddata/xcschemes/APSP.xcscheme rename to Archived Code/APSP/APSP.xcodeproj/xcshareddata/xcschemes/APSP.xcscheme diff --git a/All-Pairs Shortest Paths/APSP/APSP.xcodeproj/xcshareddata/xcschemes/APSPTests.xcscheme b/Archived Code/APSP/APSP.xcodeproj/xcshareddata/xcschemes/APSPTests.xcscheme similarity index 100% rename from All-Pairs Shortest Paths/APSP/APSP.xcodeproj/xcshareddata/xcschemes/APSPTests.xcscheme rename to Archived Code/APSP/APSP.xcodeproj/xcshareddata/xcschemes/APSPTests.xcscheme diff --git a/All-Pairs Shortest Paths/APSP/APSP.xcworkspace/contents.xcworkspacedata b/Archived Code/APSP/APSP.xcworkspace/contents.xcworkspacedata similarity index 100% rename from All-Pairs Shortest Paths/APSP/APSP.xcworkspace/contents.xcworkspacedata rename to Archived Code/APSP/APSP.xcworkspace/contents.xcworkspacedata diff --git a/All-Pairs Shortest Paths/APSP/APSP.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Archived Code/APSP/APSP.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist similarity index 100% rename from All-Pairs Shortest Paths/APSP/APSP.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist rename to Archived Code/APSP/APSP.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist diff --git a/All-Pairs Shortest Paths/APSP/APSP.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/Archived Code/APSP/APSP.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings similarity index 100% rename from All-Pairs Shortest Paths/APSP/APSP.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings rename to Archived Code/APSP/APSP.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/All-Pairs Shortest Paths/APSP/APSP/APSP.h b/Archived Code/APSP/APSP/APSP.h similarity index 100% rename from All-Pairs Shortest Paths/APSP/APSP/APSP.h rename to Archived Code/APSP/APSP/APSP.h diff --git a/All-Pairs Shortest Paths/APSP/APSP/APSP.swift b/Archived Code/APSP/APSP/APSP.swift similarity index 100% rename from All-Pairs Shortest Paths/APSP/APSP/APSP.swift rename to Archived Code/APSP/APSP/APSP.swift diff --git a/All-Pairs Shortest Paths/APSP/APSP/FloydWarshall.swift b/Archived Code/APSP/APSP/FloydWarshall.swift similarity index 100% rename from All-Pairs Shortest Paths/APSP/APSP/FloydWarshall.swift rename to Archived Code/APSP/APSP/FloydWarshall.swift diff --git a/All-Pairs Shortest Paths/APSP/APSP/Helpers.swift b/Archived Code/APSP/APSP/Helpers.swift similarity index 100% rename from All-Pairs Shortest Paths/APSP/APSP/Helpers.swift rename to Archived Code/APSP/APSP/Helpers.swift diff --git a/All-Pairs Shortest Paths/APSP/APSP/Info.plist b/Archived Code/APSP/APSP/Info.plist similarity index 100% rename from All-Pairs Shortest Paths/APSP/APSP/Info.plist rename to Archived Code/APSP/APSP/Info.plist diff --git a/All-Pairs Shortest Paths/APSP/APSPTests/APSPTests.swift b/Archived Code/APSP/APSPTests/APSPTests.swift similarity index 100% rename from All-Pairs Shortest Paths/APSP/APSPTests/APSPTests.swift rename to Archived Code/APSP/APSPTests/APSPTests.swift diff --git a/All-Pairs Shortest Paths/APSP/APSPTests/Info.plist b/Archived Code/APSP/APSPTests/Info.plist similarity index 100% rename from All-Pairs Shortest Paths/APSP/APSPTests/Info.plist rename to Archived Code/APSP/APSPTests/Info.plist diff --git a/Array2D/Array2D.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Array2D/Array2D.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Array2D/Array2D.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/SwiftAlgorithmClub/.swiftlint.yml b/SwiftAlgorithmClub/.swiftlint.yml new file mode 100644 index 000000000..4a0d0dace --- /dev/null +++ b/SwiftAlgorithmClub/.swiftlint.yml @@ -0,0 +1,94 @@ +opt_in_rules: + - array_init + - closure_spacing + - contains_over_filter_count + - contains_over_filter_is_empty + - contains_over_first_not_nil + - discouraged_object_literal + - empty_collection_literal + - empty_count + - empty_string + - explicit_init + - file_header + - first_where + - identical_operands + - implicit_return + - implicitly_unwrapped_optional + - joined_default_parameter + - last_where + - legacy_multiple + - legacy_random + - lower_acl_than_parent + - modifier_order + - multiline_parameters + - nimble_operator + - nslocalizedstring_key + - operator_usage_whitespace + - overridden_super_call + - override_in_extension + - pattern_matching_keywords + - private_action + - private_over_fileprivate + - prohibited_interface_builder + - prohibited_super_call + - quick_discouraged_call + - quick_discouraged_focused_test + - quick_discouraged_pending_test + - redundant_nil_coalescing + - single_test_class + - sorted_first_last + - sorted_imports + - toggle_bool + - trailing_closure + - unneeded_parentheses_in_closure_argument + - unowned_variable_capture + - untyped_error_in_catch + - vertical_parameter_alignment_on_call + - vertical_whitespace_opening_braces + - vertical_whitespace_closing_braces + - yoda_condition + +disabled_rules: + - identifier_name + - type_name + - line_length + - force_try + - force_cast + - function_body_length + - file_length + +file_length: 450 +nesting: + type_level: 3 +swiftlint_version: 0.36.0 + +cyclomatic_complexity: + ignores_case_statements: true + +explicit_type_interface: + excluded: + - local + +identifier_name: + excluded: + - id + +discouraged_object_literal: + color_literal: false + +trailing_closure: + only_single_muted_parameter: true + +modifier_order: + preferred_modifier_order: + - acl + - setter ACL + - override + - dynamic + - mutators + - lazy + - final + - required + - convenience + - typeMethods + - owned diff --git a/SwiftAlgorithmClub/AVLTree.playground/Contents.swift b/SwiftAlgorithmClub/AVLTree.playground/Contents.swift new file mode 100644 index 000000000..409dfdc49 --- /dev/null +++ b/SwiftAlgorithmClub/AVLTree.playground/Contents.swift @@ -0,0 +1,27 @@ +//: Playground - noun: a place where people can play + +let tree = AVLTree() + +tree.insert(key: 5, payload: "five") +print(tree) + +tree.insert(key: 4, payload: "four") +print(tree) + +tree.insert(key: 3, payload: "three") +print(tree) + +tree.insert(key: 2, payload: "two") +print(tree) + +tree.insert(key: 1, payload: "one") +print(tree) +print(tree.debugDescription) + +let node = tree.search(input: 2) // "two" + +tree.delete(key: 5) +tree.delete(key: 2) +tree.delete(key: 1) +tree.delete(key: 4) +tree.delete(key: 3) diff --git a/SwiftAlgorithmClub/AVLTree.playground/Sources/AVLTree.swift b/SwiftAlgorithmClub/AVLTree.playground/Sources/AVLTree.swift new file mode 100644 index 000000000..631fd3db6 --- /dev/null +++ b/SwiftAlgorithmClub/AVLTree.playground/Sources/AVLTree.swift @@ -0,0 +1,399 @@ +// The MIT License (MIT) + +// Copyright (c) 2016 Mike Taghavi (mitghi[at]me.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +public class TreeNode { + public typealias Node = TreeNode + + var payload: Payload? + + fileprivate var key: Key + internal var leftChild: Node? + internal var rightChild: Node? + fileprivate var height: Int + weak fileprivate var parent: Node? + + public init(key: Key, payload: Payload?, leftChild: Node?, rightChild: Node?, parent: Node?, height: Int) { + self.key = key + self.payload = payload + self.leftChild = leftChild + self.rightChild = rightChild + self.parent = parent + self.height = height + + self.leftChild?.parent = self + self.rightChild?.parent = self + } + + public convenience init(key: Key, payload: Payload?) { + self.init(key: key, payload: payload, leftChild: nil, rightChild: nil, parent: nil, height: 1) + } + + public convenience init(key: Key) { + self.init(key: key, payload: nil) + } + + var isRoot: Bool { + return parent == nil + } + + var isLeaf: Bool { + return rightChild == nil && leftChild == nil + } + + var isLeftChild: Bool { + return parent?.leftChild === self + } + + var isRightChild: Bool { + return parent?.rightChild === self + } + + var hasLeftChild: Bool { + return leftChild != nil + } + + var hasRightChild: Bool { + return rightChild != nil + } + + var hasAnyChild: Bool { + return leftChild != nil || rightChild != nil + } + + var hasBothChildren: Bool { + return leftChild != nil && rightChild != nil + } +} + +// MARK: - The AVL tree + +open class AVLTree { + public typealias Node = TreeNode + + fileprivate(set) var root: Node? + fileprivate(set) var size = 0 + + public init() { } +} + +// MARK: - Searching + +extension TreeNode { + public func minimum() -> TreeNode? { + return leftChild?.minimum() ?? self + } + + public func maximum() -> TreeNode? { + return rightChild?.maximum() ?? self + } +} + +extension AVLTree { + subscript(key: Key) -> Payload? { + get { return search(input: key) } + set { insert(key: key, payload: newValue) } + } + + public func search(input: Key) -> Payload? { + return search(key: input, node: root)?.payload + } + + fileprivate func search(key: Key, node: Node?) -> Node? { + if let node = node { + if key == node.key { + return node + } else if key < node.key { + return search(key: key, node: node.leftChild) + } else { + return search(key: key, node: node.rightChild) + } + } + return nil + } +} + +// MARK: - Inserting new items + +extension AVLTree { + public func insert(key: Key, payload: Payload? = nil) { + if let root = root { + insert(input: key, payload: payload, node: root) + } else { + root = Node(key: key, payload: payload) + } + size += 1 + } + + private func insert(input: Key, payload: Payload?, node: Node) { + if input < node.key { + if let child = node.leftChild { + insert(input: input, payload: payload, node: child) + } else { + let child = Node(key: input, payload: payload, leftChild: nil, rightChild: nil, parent: node, height: 1) + node.leftChild = child + balance(node: child) + } + } else { + if let child = node.rightChild { + insert(input: input, payload: payload, node: child) + } else { + let child = Node(key: input, payload: payload, leftChild: nil, rightChild: nil, parent: node, height: 1) + node.rightChild = child + balance(node: child) + } + } + } +} + +// MARK: - Balancing tree + +extension AVLTree { + fileprivate func updateHeightUpwards(node: Node?) { + if let node = node { + let lHeight = node.leftChild?.height ?? 0 + let rHeight = node.rightChild?.height ?? 0 + node.height = max(lHeight, rHeight) + 1 + updateHeightUpwards(node: node.parent) + } + } + + fileprivate func lrDifference(node: Node?) -> Int { + let lHeight = node?.leftChild?.height ?? 0 + let rHeight = node?.rightChild?.height ?? 0 + return lHeight - rHeight + } + + fileprivate func balance(node: Node?) { + guard let node = node else { + return + } + + updateHeightUpwards(node: node.leftChild) + updateHeightUpwards(node: node.rightChild) + + var nodes = [Node?](repeating: nil, count: 3) + var subtrees = [Node?](repeating: nil, count: 4) + let nodeParent = node.parent + + let lrFactor = lrDifference(node: node) + if lrFactor > 1 { + // left-left or left-right + if lrDifference(node: node.leftChild) > 0 { + // left-left + nodes[0] = node + nodes[2] = node.leftChild + nodes[1] = nodes[2]?.leftChild + + subtrees[0] = nodes[1]?.leftChild + subtrees[1] = nodes[1]?.rightChild + subtrees[2] = nodes[2]?.rightChild + subtrees[3] = nodes[0]?.rightChild + } else { + // left-right + nodes[0] = node + nodes[1] = node.leftChild + nodes[2] = nodes[1]?.rightChild + + subtrees[0] = nodes[1]?.leftChild + subtrees[1] = nodes[2]?.leftChild + subtrees[2] = nodes[2]?.rightChild + subtrees[3] = nodes[0]?.rightChild + } + } else if lrFactor < -1 { + // right-left or right-right + if lrDifference(node: node.rightChild) < 0 { + // right-right + nodes[1] = node + nodes[2] = node.rightChild + nodes[0] = nodes[2]?.rightChild + + subtrees[0] = nodes[1]?.leftChild + subtrees[1] = nodes[2]?.leftChild + subtrees[2] = nodes[0]?.leftChild + subtrees[3] = nodes[0]?.rightChild + } else { + // right-left + nodes[1] = node + nodes[0] = node.rightChild + nodes[2] = nodes[0]?.leftChild + + subtrees[0] = nodes[1]?.leftChild + subtrees[1] = nodes[2]?.leftChild + subtrees[2] = nodes[2]?.rightChild + subtrees[3] = nodes[0]?.rightChild + } + } else { + // Don't need to balance 'node', go for parent + balance(node: node.parent) + return + } + + // nodes[2] is always the head + + if node.isRoot { + root = nodes[2] + root?.parent = nil + } else if node.isLeftChild { + nodeParent?.leftChild = nodes[2] + nodes[2]?.parent = nodeParent + } else if node.isRightChild { + nodeParent?.rightChild = nodes[2] + nodes[2]?.parent = nodeParent + } + + nodes[2]?.leftChild = nodes[1] + nodes[1]?.parent = nodes[2] + nodes[2]?.rightChild = nodes[0] + nodes[0]?.parent = nodes[2] + + nodes[1]?.leftChild = subtrees[0] + subtrees[0]?.parent = nodes[1] + nodes[1]?.rightChild = subtrees[1] + subtrees[1]?.parent = nodes[1] + + nodes[0]?.leftChild = subtrees[2] + subtrees[2]?.parent = nodes[0] + nodes[0]?.rightChild = subtrees[3] + subtrees[3]?.parent = nodes[0] + + updateHeightUpwards(node: nodes[1]) // Update height from left + updateHeightUpwards(node: nodes[0]) // Update height from right + + balance(node: nodes[2]?.parent) + } +} + +// MARK: - Displaying tree + +extension AVLTree { + fileprivate func display(node: Node?, level: Int) { + if let node = node { + display(node: node.rightChild, level: level + 1) + print("") + if node.isRoot { + print("Root -> ", terminator: "") + } + for _ in 0.. + + + \ No newline at end of file diff --git a/SwiftAlgorithmClub/AVLTree.playground/playground.xcworkspace/contents.xcworkspacedata b/SwiftAlgorithmClub/AVLTree.playground/playground.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/SwiftAlgorithmClub/AVLTree.playground/playground.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/SwiftAlgorithmClub/AllPairsShortestPaths.playground/Contents.swift b/SwiftAlgorithmClub/AllPairsShortestPaths.playground/Contents.swift new file mode 100644 index 000000000..6566e5441 --- /dev/null +++ b/SwiftAlgorithmClub/AllPairsShortestPaths.playground/Contents.swift @@ -0,0 +1,27 @@ +var graph = AdjacencyListGraph() + +let v1 = graph.createVertex("Montreal") +let v2 = graph.createVertex("New York") +let v3 = graph.createVertex("Boston") +let v4 = graph.createVertex("Portland") +let v5 = graph.createVertex("Portsmouth") + +graph.addDirectedEdge(v1, to: v2, withWeight: 3) +graph.addDirectedEdge(v1, to: v5, withWeight: -4) +graph.addDirectedEdge(v1, to: v3, withWeight: 8) + +graph.addDirectedEdge(v2, to: v4, withWeight: 1) +graph.addDirectedEdge(v2, to: v5, withWeight: 7) + +graph.addDirectedEdge(v3, to: v2, withWeight: 4) + +graph.addDirectedEdge(v4, to: v1, withWeight: 2) +graph.addDirectedEdge(v4, to: v3, withWeight: -5) + +graph.addDirectedEdge(v5, to: v4, withWeight: 6) + +let result = FloydWarshall.apply(graph) + +let path = result.path(fromVertex: v1, toVertex: v4, inGraph: graph) + +print(graph) diff --git a/SwiftAlgorithmClub/AllPairsShortestPaths.playground/Sources/APSP.swift b/SwiftAlgorithmClub/AllPairsShortestPaths.playground/Sources/APSP.swift new file mode 100644 index 000000000..7e9a7f9cc --- /dev/null +++ b/SwiftAlgorithmClub/AllPairsShortestPaths.playground/Sources/APSP.swift @@ -0,0 +1,34 @@ +// +// Base.swift +// APSP +// +// Created by Andrew McKnight on 5/6/16. +// + +import Foundation + +/** + `APSPAlgorithm` is a protocol for encapsulating an All-Pairs Shortest Paths algorithm. + It provides a single function `apply` that accepts a subclass of `AbstractGraph` and + returns an object conforming to `APSPResult`. + */ +public protocol APSPAlgorithm { + associatedtype Q: Hashable + associatedtype P: APSPResult + + static func apply(_ graph: AbstractGraph) -> P + +} + +/** + `APSPResult` is a protocol defining functions `distance` and `path`, allowing for opaque + queries into the actual data structures that represent the APSP solution according to the algorithm used. + */ +public protocol APSPResult { + + associatedtype T: Hashable + + func distance(fromVertex from: Vertex, toVertex to: Vertex) -> Double? + func path(fromVertex from: Vertex, toVertex to: Vertex, inGraph graph: AbstractGraph) -> [T]? + +} diff --git a/SwiftAlgorithmClub/AllPairsShortestPaths.playground/Sources/AdjacencyListGraph.swift b/SwiftAlgorithmClub/AllPairsShortestPaths.playground/Sources/AdjacencyListGraph.swift new file mode 100644 index 000000000..cf5152a5f --- /dev/null +++ b/SwiftAlgorithmClub/AllPairsShortestPaths.playground/Sources/AdjacencyListGraph.swift @@ -0,0 +1,103 @@ +// +// AdjacencyListGraph.swift +// Graph +// +// Created by Andrew McKnight on 5/13/16. +// + +import Foundation + +private class EdgeList where T: Hashable { + var vertex: Vertex + var edges: [Edge]? + + init(vertex: Vertex) { + self.vertex = vertex + } + + func addEdge(_ edge: Edge) { + edges?.append(edge) + } +} + +open class AdjacencyListGraph: AbstractGraph where T: Hashable { + private var adjacencyList: [EdgeList] = [] + + public required init() { + super.init() + } + + public required init(fromGraph graph: AbstractGraph) { + super.init(fromGraph: graph) + } + + open override var vertices: [Vertex] { + adjacencyList.map { $0.vertex } + } + + open override var edges: [Edge] { + Array(adjacencyList.flatMap { $0.edges ?? [] }.reduce(into: Set>()) { $0.insert($1) }) + } + + open override func createVertex(_ data: T) -> Vertex { + if let match = vertices.first(where: { $0.data == data }) { + return match + } + + // if the vertex doesn't exist, create a new one + let vertex = Vertex(data: data, index: adjacencyList.count) + adjacencyList.append(EdgeList(vertex: vertex)) + return vertex + } + + open override func addDirectedEdge(_ from: Vertex, to: Vertex, withWeight weight: Double?) { + // works + let edge = Edge(from: from, to: to, weight: weight) + let edgeList = adjacencyList[from.index] + if edgeList.edges != nil { + edgeList.addEdge(edge) + } else { + edgeList.edges = [edge] + } + } + + open override func addUndirectedEdge(_ vertices: (Vertex, Vertex), withWeight weight: Double?) { + addDirectedEdge(vertices.0, to: vertices.1, withWeight: weight) + addDirectedEdge(vertices.1, to: vertices.0, withWeight: weight) + } + + open override func weightFrom(_ sourceVertex: Vertex, to destinationVertex: Vertex) -> Double? { + guard let edges = adjacencyList[sourceVertex.index].edges else { + return nil + } + + return edges.first { $0.to == destinationVertex }?.weight + } + + open override func edgesFrom(_ sourceVertex: Vertex) -> [Edge] { + return adjacencyList[sourceVertex.index].edges ?? [] + } + + open override var description: String { + var rows = [String]() + for edgeList in adjacencyList { + + guard let edges = edgeList.edges else { + continue + } + + var row = [String]() + for edge in edges { + var value = "\(edge.to.data)" + if edge.weight != nil { + value = "(\(value): \(edge.weight!))" + } + row.append(value) + } + + rows.append("\(edgeList.vertex.data) -> [\(row.joined(separator: ", "))]") + } + + return rows.joined(separator: "\n") + } +} diff --git a/SwiftAlgorithmClub/AllPairsShortestPaths.playground/Sources/Edge.swift b/SwiftAlgorithmClub/AllPairsShortestPaths.playground/Sources/Edge.swift new file mode 100644 index 000000000..86f22f380 --- /dev/null +++ b/SwiftAlgorithmClub/AllPairsShortestPaths.playground/Sources/Edge.swift @@ -0,0 +1,24 @@ +// +// Edge.swift +// Graph +// +// Created by Andrew McKnight on 5/8/16. +// + +public struct Edge { + public let from: Vertex + public let to: Vertex + public let weight: Double? +} + +extension Edge: CustomStringConvertible { + public var description: String { + guard let unwrappedWeight = weight else { + return "\(from.description) -> \(to.description)" + } + return "\(from.description) -(\(unwrappedWeight))-> \(to.description)" + } +} + +extension Edge: Hashable {} +extension Edge: Equatable {} diff --git a/SwiftAlgorithmClub/AllPairsShortestPaths.playground/Sources/FloydWarshall.swift b/SwiftAlgorithmClub/AllPairsShortestPaths.playground/Sources/FloydWarshall.swift new file mode 100644 index 000000000..ff9d67f00 --- /dev/null +++ b/SwiftAlgorithmClub/AllPairsShortestPaths.playground/Sources/FloydWarshall.swift @@ -0,0 +1,214 @@ +// +// FloydWarshall.swift +// APSP +// +// Created by Andrew McKnight on 5/5/16. +// + +import Foundation + +private typealias Distances = [[Double]] +private typealias Predecessors = [[Int?]] +private typealias StepResult = (distances: Distances, predecessors: Predecessors) + +/** + Encapsulation of the Floyd-Warshall All-Pairs Shortest Paths algorithm, conforming to the `APSPAlgorithm` protocol. + + - note: In all complexity bounds, `V` is the number of vertices in the graph, and `E` is the number of edges. + */ +public struct FloydWarshall: APSPAlgorithm where T: Hashable { + + public typealias Q = T + public typealias P = FloydWarshallResult + + /** + Floyd-Warshall algorithm for computing all-pairs shortest paths in a weighted directed graph. + + - precondition: `graph` must have no negative weight cycles + - complexity: `Θ(V^3)` time, `Θ(V^2)` space + - returns a `FloydWarshallResult` struct which can be queried for shortest paths and their total weights + */ + public static func apply(_ graph: AbstractGraph) -> FloydWarshallResult { + + var previousDistance = constructInitialDistanceMatrix(graph) + var previousPredecessor = constructInitialPredecessorMatrix(previousDistance) + for intermediateIdx in 0 ..< graph.vertices.count { + let nextResult = nextStep(intermediateIdx, previousDistances: previousDistance, previousPredecessors: previousPredecessor, graph: graph) + previousDistance = nextResult.distances + previousPredecessor = nextResult.predecessors + +// // uncomment to see each new weight matrix +// print(" D(\(k)):\n") +// printMatrix(nextResult.distances) +// +// // uncomment to see each new predecessor matrix +// print(" ∏(\(k)):\n") +// printIntMatrix(nextResult.predecessors) + } + return FloydWarshallResult(weights: previousDistance, predecessors: previousPredecessor) + + } + + /** + For each iteration of `intermediateIdx`, perform the comparison for the dynamic algorith, + checking for each pair of start/end vertices, whether a path taken through another vertex + produces a shorter path. + + - complexity: `Θ(V^2)` time/space + - returns: a tuple containing the next distance matrix with weights of currently known + shortest paths and the corresponding predecessor matrix + */ + static fileprivate func nextStep(_ intermediateIdx: Int, previousDistances: Distances, + previousPredecessors: Predecessors, graph: AbstractGraph) -> StepResult { + + let vertexCount = graph.vertices.count + var nextDistances = Array(repeating: Array(repeating: Double.infinity, count: vertexCount), count: vertexCount) + var nextPredecessors = Array(repeating: [Int?](repeating: nil, count: vertexCount), count: vertexCount) + + for fromIdx in 0 ..< vertexCount { + for toIndex in 0 ..< vertexCount { +// printMatrix(previousDistances, i: fromIdx, j: toIdx, k: intermediateIdx) // uncomment to see each comparison being made + let originalPathWeight = previousDistances[fromIdx][toIndex] + let newPathWeightBefore = previousDistances[fromIdx][intermediateIdx] + let newPathWeightAfter = previousDistances[intermediateIdx][toIndex] + + let minimum = min(originalPathWeight, newPathWeightBefore + newPathWeightAfter) + nextDistances[fromIdx][toIndex] = minimum + + var predecessor: Int? + if originalPathWeight <= newPathWeightBefore + newPathWeightAfter { + predecessor = previousPredecessors[fromIdx][toIndex] + } else { + predecessor = previousPredecessors[intermediateIdx][toIndex] + } + nextPredecessors[fromIdx][toIndex] = predecessor + } + } + return (nextDistances, nextPredecessors) + + } + + /** + We need to map the graph's weight domain onto the one required by the algorithm: the graph + stores either a weight as a `Double` or `nil` if no edge exists between two vertices, but + the algorithm needs a lack of an edge represented as ∞ for the `min` comparison to work correctly. + + - complexity: `Θ(V^2)` time/space + - returns: weighted adjacency matrix in form ready for processing with Floyd-Warshall + */ + static fileprivate func constructInitialDistanceMatrix(_ graph: AbstractGraph) -> Distances { + + let vertices = graph.vertices + + let vertexCount = graph.vertices.count + var distances = Array(repeating: Array(repeating: Double.infinity, count: vertexCount), count: vertexCount) + + for row in vertices { + for col in vertices { + let rowIdx = row.index + let colIdx = col.index + if rowIdx == colIdx { + distances[rowIdx][colIdx] = 0.0 + } else if let w = graph.weightFrom(row, to: col) { + distances[rowIdx][colIdx] = w + } + } + } + + return distances + + } + + /** + Make the initial predecessor index matrix. Initially each value is equal to it's row index, it's "from" index when querying into it. + + - complexity: `Θ(V^2)` time/space + */ + static fileprivate func constructInitialPredecessorMatrix(_ distances: Distances) -> Predecessors { + + let vertexCount = distances.count + var predecessors = Array(repeating: [Int?](repeating: nil, count: vertexCount), count: vertexCount) + + for fromIdx in 0 ..< vertexCount { + for toIdx in 0 ..< vertexCount { + if fromIdx != toIdx && distances[fromIdx][toIdx] < Double.infinity { + predecessors[fromIdx][toIdx] = fromIdx + } + } + } + + return predecessors + + } + +} + +/** + `FloydWarshallResult` encapsulates the result of the computation, namely the + minimized distance adjacency matrix, and the matrix of predecessor indices. + + It conforms to the `APSPResult` procotol which provides methods to retrieve + distances and paths between given pairs of start and end nodes. + */ +public struct FloydWarshallResult: APSPResult where T: Hashable { + + fileprivate var weights: Distances + fileprivate var predecessors: Predecessors + + /** + - returns: the total weight of the path from a starting vertex to a destination. + This value is the minimal connected weight between the two vertices, or `nil` if no path exists + - complexity: `Θ(1)` time/space + */ + public func distance(fromVertex from: Vertex, toVertex to: Vertex) -> Double? { + + return weights[from.index][to.index] + + } + + /** + - returns: the reconstructed path from a starting vertex to a destination, + as an array containing the data property of each vertex, or `nil` if no path exists + - complexity: `Θ(V)` time, `Θ(V^2)` space + */ + public func path(fromVertex from: Vertex, toVertex to: Vertex, inGraph graph: AbstractGraph) -> [T]? { + + if let path = recursePathFrom(fromVertex: from, toVertex: to, path: [ to ], inGraph: graph) { + let pathValues = path.map { vertex in + vertex.data + } + return pathValues + } + return nil + + } + + /** + The recursive component to rebuilding the shortest path between + two vertices using the predecessor matrix. + + - returns: the list of predecessors discovered so far + */ + fileprivate func recursePathFrom(fromVertex from: Vertex, toVertex to: Vertex, path: [Vertex], + inGraph graph: AbstractGraph) -> [Vertex]? { + + if from.index == to.index { + return [ from, to ] + } + + if let predecessor = predecessors[from.index][to.index] { + let predecessorVertex = graph.vertices[predecessor] + if predecessor == from.index { + let newPath = [ from, to ] + return newPath + } else { + let buildPath = recursePathFrom(fromVertex: from, toVertex: predecessorVertex, path: path, inGraph: graph) + let newPath = buildPath! + [ to ] + return newPath + } + } + + return nil + } + +} diff --git a/SwiftAlgorithmClub/AllPairsShortestPaths.playground/Sources/Graph.swift b/SwiftAlgorithmClub/AllPairsShortestPaths.playground/Sources/Graph.swift new file mode 100644 index 000000000..c89538360 --- /dev/null +++ b/SwiftAlgorithmClub/AllPairsShortestPaths.playground/Sources/Graph.swift @@ -0,0 +1,55 @@ +// +// Graph.swift +// Graph +// +// Created by Andrew McKnight on 5/8/16. +// + +import Foundation + +open class AbstractGraph: CustomStringConvertible { + public required init() {} + + public required init(fromGraph graph: AbstractGraph) { + for edge in graph.edges { + let from = createVertex(edge.from.data) + let to = createVertex(edge.to.data) + + addDirectedEdge(from, to: to, withWeight: edge.weight) + } + } + + open var description: String { + fatalError("abstract property accessed") + } + + open var vertices: [Vertex] { + fatalError("abstract property accessed") + } + + open var edges: [Edge] { + fatalError("abstract property accessed") + } + + // Adds a new vertex to the matrix. + // Performance: possibly O(n^2) because of the resizing of the matrix. + open func createVertex(_ data: T) -> Vertex { + fatalError("abstract function called") + } + + open func addDirectedEdge(_ from: Vertex, to: Vertex, withWeight weight: Double?) { + fatalError("abstract function called") + } + + open func addUndirectedEdge(_ vertices: (Vertex, Vertex), withWeight weight: Double?) { + fatalError("abstract function called") + } + + open func weightFrom(_ sourceVertex: Vertex, to destinationVertex: Vertex) -> Double? { + fatalError("abstract function called") + } + + open func edgesFrom(_ sourceVertex: Vertex) -> [Edge] { + fatalError("abstract function called") + } +} diff --git a/SwiftAlgorithmClub/AllPairsShortestPaths.playground/Sources/Vertex.swift b/SwiftAlgorithmClub/AllPairsShortestPaths.playground/Sources/Vertex.swift new file mode 100644 index 000000000..eecd18565 --- /dev/null +++ b/SwiftAlgorithmClub/AllPairsShortestPaths.playground/Sources/Vertex.swift @@ -0,0 +1,20 @@ +// +// Vertex.swift +// Graph +// +// Created by Andrew McKnight on 5/8/16. +// + +public struct Vertex { + public var data: T + public let index: Int +} + +extension Vertex: CustomStringConvertible { + public var description: String { + "\(index): \(data)" + } +} + +extension Vertex: Hashable {} +extension Vertex: Equatable {} diff --git a/SwiftAlgorithmClub/AllPairsShortestPaths.playground/contents.xcplayground b/SwiftAlgorithmClub/AllPairsShortestPaths.playground/contents.xcplayground new file mode 100644 index 000000000..63b6dd8df --- /dev/null +++ b/SwiftAlgorithmClub/AllPairsShortestPaths.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/SwiftAlgorithmClub/Array2D.playground/Contents.swift b/SwiftAlgorithmClub/Array2D.playground/Contents.swift new file mode 100644 index 000000000..0692465d6 --- /dev/null +++ b/SwiftAlgorithmClub/Array2D.playground/Contents.swift @@ -0,0 +1,65 @@ +/* + Two-dimensional array with a fixed number of rows and columns. + This is mostly handy for games that are played on a grid, such as chess. + Performance is always O(1). + */ +public struct Array2D { + public let columns: Int + public let rows: Int + private var array: [T] + + public init(columns: Int, rows: Int, initialValue: T) { + self.columns = columns + self.rows = rows + array = .init(repeating: initialValue, count: rows*columns) + } + + public subscript(column: Int, row: Int) -> T { + get { + precondition(column < columns, "Column \(column) Index is out of range. Array(columns: \(columns), rows:\(rows))") + precondition(row < rows, "Row \(row) Index is out of range. Array(columns: \(columns), rows:\(rows))") + return array[row*columns + column] + } + set { + precondition(column < columns, "Column \(column) Index is out of range. Array(columns: \(columns), rows:\(rows))") + precondition(row < rows, "Row \(row) Index is out of range. Array(columns: \(columns), rows:\(rows))") + array[row*columns + column] = newValue + } + } +} + +// initialization +var matrix = Array2D(columns: 3, rows: 5, initialValue: 0) + +// makes an array of rows * columns elements all filled with zero +print(matrix.array) + +// setting numbers using subscript [x, y] +matrix[0, 0] = 1 +matrix[1, 0] = 2 + +matrix[0, 1] = 3 +matrix[1, 1] = 4 + +matrix[0, 2] = 5 +matrix[1, 2] = 6 + +matrix[0, 3] = 7 +matrix[1, 3] = 8 +matrix[2, 3] = 9 + +// now the numbers are set in the array +print(matrix.array) + +// print out the 2D array with a reference around the grid +for i in 0.. + + + \ No newline at end of file diff --git a/SwiftAlgorithmClub/Array2D.playground/playground.xcworkspace/contents.xcworkspacedata b/SwiftAlgorithmClub/Array2D.playground/playground.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/SwiftAlgorithmClub/Array2D.playground/playground.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/SwiftAlgorithmClub/Array2D.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/SwiftAlgorithmClub/Array2D.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/SwiftAlgorithmClub/Array2D.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/SwiftAlgorithmClub/BTree.playground/Contents.swift b/SwiftAlgorithmClub/BTree.playground/Contents.swift new file mode 100644 index 000000000..d7cb5a6d1 --- /dev/null +++ b/SwiftAlgorithmClub/BTree.playground/Contents.swift @@ -0,0 +1,25 @@ +//: Playground - noun: a place where people can play + +import Foundation + +let bTree = BTree(order: 1)! + +bTree.insert(1, for: 1) +bTree.insert(2, for: 2) +bTree.insert(3, for: 3) +bTree.insert(4, for: 4) + +bTree.value(for: 3) +bTree[3] + +bTree.remove(2) + +bTree.traverseKeysInOrder { key in + print(key) +} + +bTree.numberOfKeys + +bTree.order + +bTree.inorderArrayFromKeys diff --git a/SwiftAlgorithmClub/BTree.playground/Sources/BTree.swift b/SwiftAlgorithmClub/BTree.playground/Sources/BTree.swift new file mode 100644 index 000000000..e1d750bc2 --- /dev/null +++ b/SwiftAlgorithmClub/BTree.playground/Sources/BTree.swift @@ -0,0 +1,592 @@ +// The MIT License (MIT) + +// Copyright (c) 2016 Viktor Szilárd Simkó (aqviktor[at]gmail.com) + +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +/* + * B-Tree + * + * A B-Tree is a self-balancing search tree, in which nodes can have more than two children. + */ + +// MARK: - BTreeNode class + +class BTreeNode { + /** + * The tree that owns the node. + */ + unowned var owner: BTree + + fileprivate var keys = [Key]() + fileprivate var values = [Value]() + var children: [BTreeNode]? + + var isLeaf: Bool { + return children == nil + } + + var numberOfKeys: Int { + return keys.count + } + + init(owner: BTree) { + self.owner = owner + } + + convenience init(owner: BTree, keys: [Key], + values: [Value], children: [BTreeNode]? = nil) { + self.init(owner: owner) + self.keys += keys + self.values += values + self.children = children + } +} + +// MARK: BTreeNode extesnion: Searching + +extension BTreeNode { + + /** + * Returns the value for a given `key`, returns nil if the `key` is not found. + * + * - Parameters: + * - key: the key of the value to be returned + */ + func value(for key: Key) -> Value? { + var index = keys.startIndex + + while (index + 1) < keys.endIndex && keys[index] < key { + index = (index + 1) + } + + if key == keys[index] { + return values[index] + } else if key < keys[index] { + return children?[index].value(for: key) + } else { + return children?[(index + 1)].value(for: key) + } + } +} + +// MARK: BTreeNode extension: Travelsals + +extension BTreeNode { + + /** + * Traverses the keys in order, executes `process` for every key. + * + * - Parameters: + * - process: the closure to be executed for every key + */ + func traverseKeysInOrder(_ process: (Key) -> Void) { + for i in 0.. owner.order * 2 { + split(child: children![index], atIndex: index) + } + } + } + + /** + * Splits `child` at `index`. + * The key-value pair at `index` gets moved up to the parent node, + * or if there is not an parent node, then a new parent node is created. + * + * - Parameters: + * - child: the child to be split + * - index: the index of the key, which will be moved up to the parent + */ + private func split(child: BTreeNode, atIndex index: Int) { + let middleIndex = child.numberOfKeys / 2 + keys.insert(child.keys[middleIndex], at: index) + values.insert(child.values[middleIndex], at: index) + child.keys.remove(at: middleIndex) + child.values.remove(at: middleIndex) + + let rightSibling = BTreeNode( + owner: owner, + keys: Array(child.keys[child.keys.indices.suffix(from: middleIndex)]), + values: Array(child.values[child.values.indices.suffix(from: middleIndex)]) + ) + child.keys.removeSubrange(child.keys.indices.suffix(from: middleIndex)) + child.values.removeSubrange(child.values.indices.suffix(from: middleIndex)) + + children!.insert(rightSibling, at: (index + 1)) + + if child.children != nil { + rightSibling.children = Array( + child.children![child.children!.indices.suffix(from: (middleIndex + 1))] + ) + child.children!.removeSubrange(child.children!.indices.suffix(from: (middleIndex + 1))) + } + } +} + +// MARK: BTreeNode extension: Removal + +/** + * An enumeration to indicate a node's position according to another node. + * + * Possible values: + * - left + * - right + */ +private enum BTreeNodePosition { + case left + case right +} + +extension BTreeNode { + private var inorderPredecessor: BTreeNode { + if isLeaf { + return self + } else { + return children!.last!.inorderPredecessor + } + } + + /** + * Removes `key` and the value associated with it from the node + * or one of its descendants. + * + * - Parameters: + * - key: the key to be removed + */ + func remove(_ key: Key) { + var index = keys.startIndex + + while (index + 1) < keys.endIndex && keys[index] < key { + index = (index + 1) + } + + if keys[index] == key { + if isLeaf { + keys.remove(at: index) + values.remove(at: index) + owner.numberOfKeys -= 1 + } else { + let predecessor = children![index].inorderPredecessor + keys[index] = predecessor.keys.last! + values[index] = predecessor.values.last! + children![index].remove(keys[index]) + if children![index].numberOfKeys < owner.order { + fix(childWithTooFewKeys: children![index], atIndex: index) + } + } + } else if key < keys[index] { + // We should go to left child... + + if let leftChild = children?[index] { + leftChild.remove(key) + if leftChild.numberOfKeys < owner.order { + fix(childWithTooFewKeys: leftChild, atIndex: index) + } + } else { + print("The key:\(key) is not in the tree.") + } + } else { + // We should go to right child... + + if let rightChild = children?[(index + 1)] { + rightChild.remove(key) + if rightChild.numberOfKeys < owner.order { + fix(childWithTooFewKeys: rightChild, atIndex: (index + 1)) + } + } else { + print("The key:\(key) is not in the tree") + } + } + } + + /** + * Fixes `childWithTooFewKeys` by either moving a key to it from + * one of its neighbouring nodes, or by merging. + * + * - Precondition: + * `childWithTooFewKeys` must have less keys than the order of the tree. + * + * - Parameters: + * - child: the child to be fixed + * - index: the index of the child to be fixed in the current node + */ + private func fix(childWithTooFewKeys child: BTreeNode, atIndex index: Int) { + + if (index - 1) >= 0 && children![(index - 1)].numberOfKeys > owner.order { + move(keyAtIndex: (index - 1), to: child, from: children![(index - 1)], at: .left) + } else if (index + 1) < children!.count && children![(index + 1)].numberOfKeys > owner.order { + move(keyAtIndex: index, to: child, from: children![(index + 1)], at: .right) + } else if (index - 1) >= 0 { + merge(child: child, atIndex: index, to: .left) + } else { + merge(child: child, atIndex: index, to: .right) + } + } + + /** + * Moves the key at the specified `index` from `node` to + * the `targetNode` at `position` + * + * - Parameters: + * - index: the index of the key to be moved in `node` + * - targetNode: the node to move the key into + * - node: the node to move the key from + * - position: the position of the from node relative to the targetNode + */ + private func move(keyAtIndex index: Int, to targetNode: BTreeNode, + from node: BTreeNode, at position: BTreeNodePosition) { + switch position { + case .left: + targetNode.keys.insert(keys[index], at: targetNode.keys.startIndex) + targetNode.values.insert(values[index], at: targetNode.values.startIndex) + keys[index] = node.keys.last! + values[index] = node.values.last! + node.keys.removeLast() + node.values.removeLast() + if !targetNode.isLeaf { + targetNode.children!.insert(node.children!.last!, + at: targetNode.children!.startIndex) + node.children!.removeLast() + } + + case .right: + targetNode.keys.insert(keys[index], at: targetNode.keys.endIndex) + targetNode.values.insert(values[index], at: targetNode.values.endIndex) + keys[index] = node.keys.first! + values[index] = node.values.first! + node.keys.removeFirst() + node.values.removeFirst() + if !targetNode.isLeaf { + targetNode.children!.insert(node.children!.first!, + at: targetNode.children!.endIndex) + node.children!.removeFirst() + } + } + } + + /** + * Merges `child` at `position` to the node at the `position`. + * + * - Parameters: + * - child: the child to be merged + * - index: the index of the child in the current node + * - position: the position of the node to merge into + */ + private func merge(child: BTreeNode, atIndex index: Int, to position: BTreeNodePosition) { + switch position { + case .left: + // We can merge to the left sibling + + children![(index - 1)].keys = children![(index - 1)].keys + + [keys[(index - 1)]] + child.keys + + children![(index - 1)].values = children![(index - 1)].values + + [values[(index - 1)]] + child.values + + keys.remove(at: (index - 1)) + values.remove(at: (index - 1)) + + if !child.isLeaf { + children![(index - 1)].children = + children![(index - 1)].children! + child.children! + } + + case .right: + // We should merge to the right sibling + + children![(index + 1)].keys = child.keys + [keys[index]] + + children![(index + 1)].keys + + children![(index + 1)].values = child.values + [values[index]] + + children![(index + 1)].values + + keys.remove(at: index) + values.remove(at: index) + + if !child.isLeaf { + children![(index + 1)].children = + child.children! + children![(index + 1)].children! + } + } + children!.remove(at: index) + } +} + +// MARK: BTreeNode extension: Conversion + +extension BTreeNode { + /** + * Returns an array which contains the keys from the current node + * and its descendants in order. + */ + var inorderArrayFromKeys: [Key] { + var array = [Key]() + + for i in 0.. { + /** + * The order of the B-Tree + * + * The number of keys in every node should be in the [order, 2*order] range, + * except the root node which is allowed to contain less keys than the value of order. + */ + public let order: Int + + /** + * The root node of the tree + */ + var rootNode: BTreeNode! + + fileprivate(set) public var numberOfKeys = 0 + + /** + * Designated initializer for the tree + * + * - Parameters: + * - order: The order of the tree. + */ + public init?(order: Int) { + guard order > 0 else { + print("Order has to be greater than 0.") + return nil + } + self.order = order + rootNode = BTreeNode(owner: self) + } +} + +// MARK: BTree extension: Travelsals + +extension BTree { + /** + * Traverses the keys in order, executes `process` for every key. + * + * - Parameters: + * - process: the closure to be executed for every key + */ + public func traverseKeysInOrder(_ process: (Key) -> Void) { + rootNode.traverseKeysInOrder(process) + } +} + +// MARK: BTree extension: Subscript + +extension BTree { + /** + * Returns the value for a given `key`, returns nil if the `key` is not found. + * + * - Parameters: + * - key: the key of the value to be returned + */ + public subscript (key: Key) -> Value? { + return value(for: key) + } +} + +// MARK: BTree extension: Value for Key + +extension BTree { + /** + * Returns the value for a given `key`, returns nil if the `key` is not found. + * + * - Parameters: + * - key: the key of the value to be returned + */ + public func value(for key: Key) -> Value? { + guard rootNode.numberOfKeys > 0 else { + return nil + } + + return rootNode.value(for: key) + } +} + +// MARK: BTree extension: Insertion + +extension BTree { + /** + * Inserts the `value` for the `key` into the tree. + * + * - Parameters: + * - value: the value to be inserted for `key` + * - key: the key for the `value` + */ + public func insert(_ value: Value, for key: Key) { + rootNode.insert(value, for: key) + + if rootNode.numberOfKeys > order * 2 { + splitRoot() + } + } + + /** + * Splits the root node of the tree. + * + * - Precondition: + * The root node of the tree contains `order` * 2 keys. + */ + private func splitRoot() { + let middleIndexOfOldRoot = rootNode.numberOfKeys / 2 + + let newRoot = BTreeNode( + owner: self, + keys: [rootNode.keys[middleIndexOfOldRoot]], + values: [rootNode.values[middleIndexOfOldRoot]], + children: [rootNode] + ) + rootNode.keys.remove(at: middleIndexOfOldRoot) + rootNode.values.remove(at: middleIndexOfOldRoot) + + let newRightChild = BTreeNode( + owner: self, + keys: Array(rootNode.keys[rootNode.keys.indices.suffix(from: middleIndexOfOldRoot)]), + values: Array(rootNode.values[rootNode.values.indices.suffix(from: middleIndexOfOldRoot)]) + ) + rootNode.keys.removeSubrange(rootNode.keys.indices.suffix(from: middleIndexOfOldRoot)) + rootNode.values.removeSubrange(rootNode.values.indices.suffix(from: middleIndexOfOldRoot)) + + if rootNode.children != nil { + newRightChild.children = Array( + rootNode.children![rootNode.children!.indices.suffix(from: (middleIndexOfOldRoot + 1))] + ) + rootNode.children!.removeSubrange( + rootNode.children!.indices.suffix(from: (middleIndexOfOldRoot + 1)) + ) + } + + newRoot.children!.append(newRightChild) + rootNode = newRoot + } +} + +// MARK: BTree extension: Removal + +extension BTree { + /** + * Removes `key` and the value associated with it from the tree. + * + * - Parameters: + * - key: the key to remove + */ + public func remove(_ key: Key) { + guard rootNode.numberOfKeys > 0 else { + return + } + + rootNode.remove(key) + + if rootNode.numberOfKeys == 0 && !rootNode.isLeaf { + rootNode = rootNode.children!.first! + } + } +} + +// MARK: BTree extension: Conversion + +extension BTree { + /** + * The keys of the tree in order. + */ + public var inorderArrayFromKeys: [Key] { + return rootNode.inorderArrayFromKeys + } +} + +// MARK: BTree extension: Decription + +extension BTree: CustomStringConvertible { + /** + * Returns a String containing the preorder representation of the nodes. + */ + public var description: String { + return rootNode.description + } +} diff --git a/SwiftAlgorithmClub/BTree.playground/contents.xcplayground b/SwiftAlgorithmClub/BTree.playground/contents.xcplayground new file mode 100644 index 000000000..06828af92 --- /dev/null +++ b/SwiftAlgorithmClub/BTree.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/SwiftAlgorithmClub/BTree.playground/playground.xcworkspace/contents.xcworkspacedata b/SwiftAlgorithmClub/BTree.playground/playground.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/SwiftAlgorithmClub/BTree.playground/playground.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/SwiftAlgorithmClub/BTree.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/SwiftAlgorithmClub/BTree.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/SwiftAlgorithmClub/BTree.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/SwiftAlgorithmClub/BinarySearch.playground/Contents.swift b/SwiftAlgorithmClub/BinarySearch.playground/Contents.swift new file mode 100644 index 000000000..f673f25c4 --- /dev/null +++ b/SwiftAlgorithmClub/BinarySearch.playground/Contents.swift @@ -0,0 +1,19 @@ +//: Playground - noun: a place where people can play + +// An unsorted array of numbers +let numbers = [11, 59, 3, 2, 53, 17, 31, 7, 19, 67, 47, 13, 37, 61, 29, 43, 5, 41, 23] + +// Binary search requires that the array is sorted from low to high +let sorted = numbers.sorted() + +// Using recursive solution +binarySearch(sorted, key: 2, range: 0 ..< sorted.count) // gives 0 +binarySearch(sorted, key: 67, range: 0 ..< sorted.count) // gives 18 +binarySearch(sorted, key: 43, range: 0 ..< sorted.count) // gives 13 +binarySearch(sorted, key: 42, range: 0 ..< sorted.count) // nil + +// Using iterative solution +binarySearch(sorted, key: 2) // gives 0 +binarySearch(sorted, key: 67) // gives 18 +binarySearch(sorted, key: 43) // gives 13 +binarySearch(sorted, key: 42) // nil diff --git a/SwiftAlgorithmClub/BinarySearch.playground/Sources/BinarySearch.swift b/SwiftAlgorithmClub/BinarySearch.playground/Sources/BinarySearch.swift new file mode 100644 index 000000000..d6623e355 --- /dev/null +++ b/SwiftAlgorithmClub/BinarySearch.playground/Sources/BinarySearch.swift @@ -0,0 +1,52 @@ +/** + Binary Search + + Recursively splits the array in half until the value is found. + + If there is more than one occurrence of the search key in the array, then + there is no guarantee which one it finds. + + Note: The array must be sorted! + **/ + +import Foundation + +// The recursive version of binary search. + +public func binarySearch(_ a: [T], key: T, range: Range) -> Int? { + if range.lowerBound >= range.upperBound { + return nil + } else { + let midIndex = range.lowerBound + (range.upperBound - range.lowerBound) / 2 + if a[midIndex] > key { + return binarySearch(a, key: key, range: range.lowerBound ..< midIndex) + } else if a[midIndex] < key { + return binarySearch(a, key: key, range: midIndex + 1 ..< range.upperBound) + } else { + return midIndex + } + } +} + +/** + The iterative version of binary search. + + Notice how similar these functions are. The difference is that this one + uses a while loop, while the other calls itself recursively. + **/ + +public func binarySearch(_ a: [T], key: T) -> Int? { + var lowerBound = 0 + var upperBound = a.count + while lowerBound < upperBound { + let midIndex = lowerBound + (upperBound - lowerBound) / 2 + if a[midIndex] == key { + return midIndex + } else if a[midIndex] < key { + lowerBound = midIndex + 1 + } else { + upperBound = midIndex + } + } + return nil +} diff --git a/SwiftAlgorithmClub/BinarySearch.playground/contents.xcplayground b/SwiftAlgorithmClub/BinarySearch.playground/contents.xcplayground new file mode 100644 index 000000000..69d154d1e --- /dev/null +++ b/SwiftAlgorithmClub/BinarySearch.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/SwiftAlgorithmClub/BinarySearchTree1.playground/Contents.swift b/SwiftAlgorithmClub/BinarySearchTree1.playground/Contents.swift new file mode 100644 index 000000000..ec9902283 --- /dev/null +++ b/SwiftAlgorithmClub/BinarySearchTree1.playground/Contents.swift @@ -0,0 +1,59 @@ +//: Playground - noun: a place where people can play + +let tree = BinarySearchTree(value: 7) +tree.insert(value: 2) +tree.insert(value: 5) +tree.insert(value: 10) +tree.insert(value: 9) +tree.insert(value: 1) + +let toDelete = tree.search(value: 1) +toDelete?.remove() +tree + +let tree2 = BinarySearchTree(array: [7, 2, 5, 10, 9, 1]) + +tree.search(value: 5) +tree.search(value: 2) +tree.search(value: 7) +tree.search(value: 6) + +tree.traverseInOrder { value in print(value) } + +tree.toArray() + +tree.minimum() +tree.maximum() + +if let node2 = tree.search(value: 2) { + node2.remove() + node2 + print(tree) +} + +tree.height() +tree.predecessor() +tree.successor() + +if let node10 = tree.search(value: 10) { + node10.depth() // 1 + node10.height() // 1 + node10.predecessor() + node10.successor() // nil +} + +if let node9 = tree.search(value: 9) { + node9.depth() // 2 + node9.height() // 0 + node9.predecessor() + node9.successor() +} + +if let node1 = tree.search(value: 1) { + // This makes it an invalid binary search tree because 100 is greater + // than the root, 7, and so must be in the right branch not in the left. + tree.isBST(minValue: Int.min, maxValue: Int.max) // true + node1.insert(value: 100) + tree.search(value: 100) // nil + tree.isBST(minValue: Int.min, maxValue: Int.max) // false +} diff --git a/SwiftAlgorithmClub/BinarySearchTree1.playground/Sources/BinarySearchTree.swift b/SwiftAlgorithmClub/BinarySearchTree1.playground/Sources/BinarySearchTree.swift new file mode 100644 index 000000000..baca04767 --- /dev/null +++ b/SwiftAlgorithmClub/BinarySearchTree1.playground/Sources/BinarySearchTree.swift @@ -0,0 +1,346 @@ +/* + A binary search tree. + + Each node stores a value and two children. The left child contains a smaller + value; the right a larger (or equal) value. + + This tree allows duplicate elements. + + This tree does not automatically balance itself. To make sure it is balanced, + you should insert new values in randomized order, not in sorted order. +*/ +public class BinarySearchTree { + fileprivate(set) public var value: T + fileprivate(set) public var parent: BinarySearchTree? + fileprivate(set) public var left: BinarySearchTree? + fileprivate(set) public var right: BinarySearchTree? + + public init(value: T) { + self.value = value + } + + public convenience init(array: [T]) { + precondition(array.count > 0) + self.init(value: array.first!) + for v in array.dropFirst() { + insert(value: v) + } + } + + public var isRoot: Bool { + return parent == nil + } + + public var isLeaf: Bool { + return left == nil && right == nil + } + + public var isLeftChild: Bool { + return parent?.left === self + } + + public var isRightChild: Bool { + return parent?.right === self + } + + public var hasLeftChild: Bool { + return left != nil + } + + public var hasRightChild: Bool { + return right != nil + } + + public var hasAnyChild: Bool { + return hasLeftChild || hasRightChild + } + + public var hasBothChildren: Bool { + return hasLeftChild && hasRightChild + } + + /* How many nodes are in this subtree. Performance: O(n). */ + public var count: Int { + return (left?.count ?? 0) + 1 + (right?.count ?? 0) + } +} + +// MARK: - Adding items + +extension BinarySearchTree { + /* + Inserts a new element into the tree. You should only insert elements + at the root, to make to sure this remains a valid binary tree! + Performance: runs in O(h) time, where h is the height of the tree. + */ + public func insert(value: T) { + if value < self.value { + if let left = left { + left.insert(value: value) + } else { + left = BinarySearchTree(value: value) + left?.parent = self + } + } else { + if let right = right { + right.insert(value: value) + } else { + right = BinarySearchTree(value: value) + right?.parent = self + } + } + } +} + +// MARK: - Deleting items + +extension BinarySearchTree { + /* + Deletes a node from the tree. + + Returns the node that has replaced this removed one (or nil if this was a + leaf node). That is primarily useful for when you delete the root node, in + which case the tree gets a new root. + + Performance: runs in O(h) time, where h is the height of the tree. + */ + @discardableResult public func remove() -> BinarySearchTree? { + let replacement: BinarySearchTree? + + // Replacement for current node can be either biggest one on the left or + // smallest one on the right, whichever is not nil + if let right = right { + replacement = right.minimum() + } else if let left = left { + replacement = left.maximum() + } else { + replacement = nil + } + + replacement?.remove() + + // Place the replacement on current node's position + replacement?.right = right + replacement?.left = left + right?.parent = replacement + left?.parent = replacement + reconnectParentTo(node: replacement) + + // The current node is no longer part of the tree, so clean it up. + parent = nil + left = nil + right = nil + + return replacement + } + + private func reconnectParentTo(node: BinarySearchTree?) { + if let parent = parent { + if isLeftChild { + parent.left = node + } else { + parent.right = node + } + } + node?.parent = parent + } +} + +// MARK: - Searching + +extension BinarySearchTree { + /* + Finds the "highest" node with the specified value. + Performance: runs in O(h) time, where h is the height of the tree. + */ + public func search(value: T) -> BinarySearchTree? { + var node: BinarySearchTree? = self + while let n = node { + if value < n.value { + node = n.left + } else if value > n.value { + node = n.right + } else { + return node + } + } + return nil + } + + /* + // Recursive version of search + public func search(value: T) -> BinarySearchTree? { + if value < self.value { + return left?.search(value) + } else if value > self.value { + return right?.search(value) + } else { + return self // found it! + } + } + */ + + public func contains(value: T) -> Bool { + return search(value: value) != nil + } + + /* + Returns the leftmost descendent. O(h) time. + */ + public func minimum() -> BinarySearchTree { + var node = self + while let next = node.left { + node = next + } + return node + } + + /* + Returns the rightmost descendent. O(h) time. + */ + public func maximum() -> BinarySearchTree { + var node = self + while let next = node.right { + node = next + } + return node + } + + /* + Calculates the depth of this node, i.e. the distance to the root. + Takes O(h) time. + */ + public func depth() -> Int { + var node = self + var edges = 0 + while let parent = node.parent { + node = parent + edges += 1 + } + return edges + } + + /* + Calculates the height of this node, i.e. the distance to the lowest leaf. + Since this looks at all children of this node, performance is O(n). + */ + public func height() -> Int { + if isLeaf { + return 0 + } else { + return 1 + max(left?.height() ?? 0, right?.height() ?? 0) + } + } + + /* + Finds the node whose value precedes our value in sorted order. + */ + public func predecessor() -> BinarySearchTree? { + if let left = left { + return left.maximum() + } else { + var node = self + while let parent = node.parent { + if parent.value < value { return parent } + node = parent + } + return nil + } + } + + /* + Finds the node whose value succeeds our value in sorted order. + */ + public func successor() -> BinarySearchTree? { + if let right = right { + return right.minimum() + } else { + var node = self + while let parent = node.parent { + if parent.value > value { return parent } + node = parent + } + return nil + } + } +} + +// MARK: - Traversal + +extension BinarySearchTree { + public func traverseInOrder(process: (T) -> Void) { + left?.traverseInOrder(process: process) + process(value) + right?.traverseInOrder(process: process) + } + + public func traversePreOrder(process: (T) -> Void) { + process(value) + left?.traversePreOrder(process: process) + right?.traversePreOrder(process: process) + } + + public func traversePostOrder(process: (T) -> Void) { + left?.traversePostOrder(process: process) + right?.traversePostOrder(process: process) + process(value) + } + + /* + Performs an in-order traversal and collects the results in an array. + */ + public func map(formula: (T) -> T) -> [T] { + var a = [T]() + if let left = left { a += left.map(formula: formula) } + a.append(formula(value)) + if let right = right { a += right.map(formula: formula) } + return a + } +} + +/* + Is this binary tree a valid binary search tree? +*/ +extension BinarySearchTree { + public func isBST(minValue: T, maxValue: T) -> Bool { + if value < minValue || value > maxValue { return false } + let leftBST = left?.isBST(minValue: minValue, maxValue: value) ?? true + let rightBST = right?.isBST(minValue: value, maxValue: maxValue) ?? true + return leftBST && rightBST + } +} + +// MARK: - Debugging + +extension BinarySearchTree: CustomStringConvertible { + public var description: String { + var s = "" + if let left = left { + s += "(\(left.description)) <- " + } + s += "\(value)" + if let right = right { + s += " -> (\(right.description))" + } + return s + } + + public func toArray() -> [T] { + return map { $0 } + } + +} + +//extension BinarySearchTree: CustomDebugStringConvertible { +// public var debugDescription: String { +// var s = "" +// if let left = left { +// s += "(\(left.description)) <- " +// } +// s += "\(value)" +// if let right = right { +// s += " -> (\(right.description))" +// } +// return s +// } +//} diff --git a/SwiftAlgorithmClub/BinarySearchTree1.playground/contents.xcplayground b/SwiftAlgorithmClub/BinarySearchTree1.playground/contents.xcplayground new file mode 100644 index 000000000..06828af92 --- /dev/null +++ b/SwiftAlgorithmClub/BinarySearchTree1.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/SwiftAlgorithmClub/BinarySearchTree1.playground/playground.xcworkspace/contents.xcworkspacedata b/SwiftAlgorithmClub/BinarySearchTree1.playground/playground.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/SwiftAlgorithmClub/BinarySearchTree1.playground/playground.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/SwiftAlgorithmClub/BinarySearchTree1.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/SwiftAlgorithmClub/BinarySearchTree1.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/SwiftAlgorithmClub/BinarySearchTree1.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/SwiftAlgorithmClub/BinarySearchTree2.playground/Contents.swift b/SwiftAlgorithmClub/BinarySearchTree2.playground/Contents.swift new file mode 100644 index 000000000..2b5dcc341 --- /dev/null +++ b/SwiftAlgorithmClub/BinarySearchTree2.playground/Contents.swift @@ -0,0 +1,14 @@ +//: Playground - noun: a place where people can play + +// Each time you insert something, you get back a completely new tree. +var tree = BinarySearchTree.leaf(7) +tree = tree.insert(newValue: 2) +tree = tree.insert(newValue: 5) +tree = tree.insert(newValue: 10) +tree = tree.insert(newValue: 9) +tree = tree.insert(newValue: 1) +print(tree) + +tree.search(x: 10) +tree.search(x: 1) +tree.search(x: 11) diff --git a/SwiftAlgorithmClub/BinarySearchTree2.playground/Sources/BinarySearchTree.swift b/SwiftAlgorithmClub/BinarySearchTree2.playground/Sources/BinarySearchTree.swift new file mode 100644 index 000000000..5128bb348 --- /dev/null +++ b/SwiftAlgorithmClub/BinarySearchTree2.playground/Sources/BinarySearchTree.swift @@ -0,0 +1,121 @@ +/* + Binary search tree using value types + + The tree is immutable. Any insertions or deletions will create a new tree. +*/ +public enum BinarySearchTree { + case empty + case leaf(T) + indirect case node(BinarySearchTree, T, BinarySearchTree) + + /* How many nodes are in this subtree. Performance: O(n). */ + public var count: Int { + switch self { + case .empty: return 0 + case .leaf: return 1 + case let .node(left, _, right): return left.count + 1 + right.count + } + } + + /* Distance of this node to its lowest leaf. Performance: O(n). */ + public var height: Int { + switch self { + case .empty: return 0 + case .leaf: return 1 + case let .node(left, _, right): return 1 + max(left.height, right.height) + } + } + + /* + Inserts a new element into the tree. + Performance: runs in O(h) time, where h is the height of the tree. + */ + public func insert(newValue: T) -> BinarySearchTree { + switch self { + case .empty: + return .leaf(newValue) + + case .leaf(let value): + if newValue < value { + return .node(.leaf(newValue), value, .empty) + } else { + return .node(.empty, value, .leaf(newValue)) + } + + case .node(let left, let value, let right): + if newValue < value { + return .node(left.insert(newValue: newValue), value, right) + } else { + return .node(left, value, right.insert(newValue: newValue)) + } + } + } + + /* + Finds the "highest" node with the specified value. + Performance: runs in O(h) time, where h is the height of the tree. + */ + public func search(x: T) -> BinarySearchTree? { + switch self { + case .empty: + return nil + case .leaf(let y): + return (x == y) ? self : nil + case let .node(left, y, right): + if x < y { + return left.search(x: x) + } else if y < x { + return right.search(x: x) + } else { + return self + } + } + } + + public func contains(x: T) -> Bool { + return search(x: x) != nil + } + + /* + Returns the leftmost descendent. O(h) time. + */ + public func minimum() -> BinarySearchTree { + var node = self + var prev = node + while case let .node(next, _, _) = node { + prev = node + node = next + } + if case .leaf = node { + return node + } + return prev + } + + /* + Returns the rightmost descendent. O(h) time. + */ + public func maximum() -> BinarySearchTree { + var node = self + var prev = node + while case let .node(_, _, next) = node { + prev = node + node = next + } + if case .leaf = node { + return node + } + return prev + } +} + +extension BinarySearchTree: CustomDebugStringConvertible { + public var debugDescription: String { + switch self { + case .empty: return "." + case .leaf(let value): return "\(value)" + case .node(let left, let value, let right): + return "(\(left.debugDescription) <- \(value) -> \(right.debugDescription))" + } + } +} diff --git a/SwiftAlgorithmClub/BinarySearchTree2.playground/contents.xcplayground b/SwiftAlgorithmClub/BinarySearchTree2.playground/contents.xcplayground new file mode 100644 index 000000000..06828af92 --- /dev/null +++ b/SwiftAlgorithmClub/BinarySearchTree2.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/SwiftAlgorithmClub/BinarySearchTree2.playground/playground.xcworkspace/contents.xcworkspacedata b/SwiftAlgorithmClub/BinarySearchTree2.playground/playground.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/SwiftAlgorithmClub/BinarySearchTree2.playground/playground.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/SwiftAlgorithmClub/BinarySearchTree2.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/SwiftAlgorithmClub/BinarySearchTree2.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/SwiftAlgorithmClub/BinarySearchTree2.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/SwiftAlgorithmClub/BinaryTree.playground/Contents.swift b/SwiftAlgorithmClub/BinaryTree.playground/Contents.swift new file mode 100644 index 000000000..b0bff577a --- /dev/null +++ b/SwiftAlgorithmClub/BinaryTree.playground/Contents.swift @@ -0,0 +1,79 @@ +//: Playground - noun: a place where people can play + +public indirect enum BinaryTree { + case node(BinaryTree, T, BinaryTree) + case empty + + public var count: Int { + switch self { + case let .node(left, _, right): + return left.count + 1 + right.count + case .empty: + return 0 + } + } +} + +extension BinaryTree: CustomStringConvertible { + public var description: String { + switch self { + case let .node(left, value, right): + return "value: \(value), left = [\(left.description)], right = [\(right.description)]" + case .empty: + return "" + } + } +} + +// leaf nodes +let node5 = BinaryTree.node(.empty, "5", .empty) +let nodeA = BinaryTree.node(.empty, "a", .empty) +let node10 = BinaryTree.node(.empty, "10", .empty) +let node4 = BinaryTree.node(.empty, "4", .empty) +let node3 = BinaryTree.node(.empty, "3", .empty) +let nodeB = BinaryTree.node(.empty, "b", .empty) + +// intermediate nodes on the left +let aMinus10 = BinaryTree.node(nodeA, "-", node10) +let timesLeft = BinaryTree.node(node5, "*", aMinus10) + +// intermediate nodes on the right +let minus4 = BinaryTree.node(.empty, "-", node4) +let divide3andB = BinaryTree.node(node3, "/", nodeB) +let timesRight = BinaryTree.node(minus4, "*", divide3andB) + +// root node +let tree = BinaryTree.node(timesLeft, "+", timesRight) + +print(tree) +tree.count // 12 + +extension BinaryTree { + public func traverseInOrder(process: (T) -> Void) { + if case let .node(left, value, right) = self { + left.traverseInOrder(process: process) + process(value) + right.traverseInOrder(process: process) + } + } + + public func traversePreOrder(process: (T) -> Void) { + if case let .node(left, value, right) = self { + process(value) + left.traversePreOrder(process: process) + right.traversePreOrder(process: process) + } + } + + public func traversePostOrder(process: (T) -> Void) { + if case let .node(left, value, right) = self { + left.traversePostOrder(process: process) + right.traversePostOrder(process: process) + process(value) + } + } +} + +//tree.traverseInOrder { s in print(s) } +//tree.traversePreOrder { s in print(s) } +tree.traversePostOrder { s in print(s) } diff --git a/SwiftAlgorithmClub/BinaryTree.playground/contents.xcplayground b/SwiftAlgorithmClub/BinaryTree.playground/contents.xcplayground new file mode 100644 index 000000000..69d154d1e --- /dev/null +++ b/SwiftAlgorithmClub/BinaryTree.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/SwiftAlgorithmClub/BinaryTree.playground/playground.xcworkspace/contents.xcworkspacedata b/SwiftAlgorithmClub/BinaryTree.playground/playground.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/SwiftAlgorithmClub/BinaryTree.playground/playground.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/SwiftAlgorithmClub/BinaryTree.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/SwiftAlgorithmClub/BinaryTree.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/SwiftAlgorithmClub/BinaryTree.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/SwiftAlgorithmClub/BitSet.playground/Contents.swift b/SwiftAlgorithmClub/BitSet.playground/Contents.swift new file mode 100644 index 000000000..70d5ca5e8 --- /dev/null +++ b/SwiftAlgorithmClub/BitSet.playground/Contents.swift @@ -0,0 +1,111 @@ +//: Playground - noun: a place where people can play + +// Create a bit set that stores 140 bits +var bits = BitSet(size: 140) + +// Initially, it looks like all zeros: +// 0000000000000000000000000000000000000000000000000000000000000000 +// 0000000000000000000000000000000000000000000000000000000000000000 +// 0000000000000000000000000000000000000000000000000000000000000000 +print(bits) + +bits[2] = true +bits[99] = true +bits[128] = true +bits.cardinality // 3 + +// 0010000000000000000000000000000000000000000000000000000000000000 +// 0000000000000000000000000000000000010000000000000000000000000000 +// 1000000000000000000000000000000000000000000000000000000000000000 +print(bits) + +bits.setAll() +bits.cardinality // 140 + +// 1111111111111111111111111111111111111111111111111111111111111111 +// 1111111111111111111111111111111111111111111111111111111111111111 +// 1111111111110000000000000000000000000000000000000000000000000000 +print(bits) + +print("") + +// Bitwise operations + +var a = BitSet(size: 4) +var b = BitSet(size: 8) + +a.setAll() +a.cardinality // 4 + +a[1] = false +a[2] = false +a[3] = false + +b[2] = true +b[6] = true +b[7] = true + +print(a) // 1000000000000000000000000000000000000000000000000000000000000000 +print(b) // 0010001100000000000000000000000000000000000000000000000000000000 + +let c = a | b +c.size // 8 + +print(c) // 1010001100000000000000000000000000000000000000000000000000000000 + +a.cardinality // 1 +b.cardinality // 3 +c.cardinality // 4 + +print("") +print(~a) // 0111000000000000000000000000000000000000000000000000000000000000 +print(~b) // 1101110000000000000000000000000000000000000000000000000000000000 +print(~c) // 0101110000000000000000000000000000000000000000000000000000000000 + +(~a).cardinality // 3 +(~b).cardinality // 5 +(~c).cardinality // 4 + +var z = BitSet(size: 66) +z.all0() // true +z.all1() // false + +z[45] = true +z.any1() // true +z.all0() // false + +z[45] = false +z.any1() // false +z.all0() // true + +z[65] = true +z.any1() // true + +z.setAll() +z.all1() // true + +z[65] = false +z.all1() // false + +//var bigBits = BitSet(size: 10000) +//print(bigBits) + +var smallBitSet = BitSet(size: 16) +smallBitSet[5] = true +smallBitSet[10] = true +print(smallBitSet >> 3) +print(smallBitSet << 6) // one bit shifts off the end + +var bigBitSet = BitSet( size: 120 ) +bigBitSet[1] = true +bigBitSet[3] = true +bigBitSet[7] = true +bigBitSet[32] = true +bigBitSet[55] = true +bigBitSet[64] = true +bigBitSet[80] = true +print(bigBitSet) +print(bigBitSet << 32) +print(bigBitSet << 64) +print(bigBitSet >> 32) +print(bigBitSet >> 64) diff --git a/SwiftAlgorithmClub/BitSet.playground/Sources/BitSet.swift b/SwiftAlgorithmClub/BitSet.playground/Sources/BitSet.swift new file mode 100644 index 000000000..c7d97437c --- /dev/null +++ b/SwiftAlgorithmClub/BitSet.playground/Sources/BitSet.swift @@ -0,0 +1,273 @@ +/* + A fixed-size sequence of n bits. Bits have indices 0 to n-1. +*/ +public struct BitSet { + /* How many bits this object can hold. */ + private(set) public var size: Int + + /* + We store the bits in a list of unsigned 64-bit integers. + The first entry, `words[0]`, is the least significant word. + */ + fileprivate let N = 64 + public typealias Word = UInt64 + fileprivate(set) public var words: [Word] + + private let allOnes = ~Word() + + /* Creates a bit set that can hold `size` bits. All bits are initially 0. */ + public init(size: Int) { + precondition(size > 0) + self.size = size + + // Round up the count to the next multiple of 64. + let n = (size + (N-1)) / N + words = [Word](repeating: 0, count: n) + } + + /* Converts a bit index into an array index and a mask inside the word. */ + private func indexOf(_ i: Int) -> (Int, Word) { + precondition(i >= 0) + precondition(i < size) + let o = i / N + let m = Word(i - o*N) + return (o, 1 << m) + } + + /* Returns a mask that has 1s for all bits that are in the last word. */ + private func lastWordMask() -> Word { + let diff = words.count*N - size + if diff > 0 { + // Set the highest bit that's still valid. + let mask = 1 << Word(63 - diff) + // Subtract 1 to turn it into a mask, and add the high bit back in. + return (Word)(mask | (mask - 1)) + } else { + return allOnes + } + } + + /* + If the size is not a multiple of N, then we have to clear out the bits + that we're not using, or bitwise operations between two differently sized + BitSets will go wrong. + */ + fileprivate mutating func clearUnusedBits() { + words[words.count - 1] &= lastWordMask() + } + + /* So you can write bitset[99] = ... */ + public subscript(i: Int) -> Bool { + get { return isSet(i) } + set { if newValue { set(i) } else { clear(i) } } + } + + /* Sets the bit at the specified index to 1. */ + public mutating func set(_ i: Int) { + let (j, m) = indexOf(i) + words[j] |= m + } + + /* Sets all the bits to 1. */ + public mutating func setAll() { + for i in 0.. Bool { + let (j, m) = indexOf(i) + words[j] ^= m + return (words[j] & m) != 0 + } + + /* Determines whether the bit at the specific index is 1 (true) or 0 (false). */ + public func isSet(_ i: Int) -> Bool { + let (j, m) = indexOf(i) + return (words[j] & m) != 0 + } + + /* + Returns the number of bits that are 1. Time complexity is O(s) where s is + the number of 1-bits. + */ + public var cardinality: Int { + var count = 0 + for var x in words { + while x != 0 { + let y = x & ~(x - 1) // find lowest 1-bit + x = x ^ y // and erase it + count += 1 + } + } + return count + } + + /* Checks if all the bits are set. */ + public func all1() -> Bool { + guard words.allSatisfy({ $0 == allOnes }) else { return false } + return words[words.count - 1] == lastWordMask() + } + + /* Checks if any of the bits are set. */ + public func any1() -> Bool { + guard words.allSatisfy({ $0 == 0 }) else { return true } + return false + } + + /* Checks if none of the bits are set. */ + public func all0() -> Bool { + guard words.allSatisfy({ $0 == 0 }) else { return false } + return true + } +} + +// MARK: - Equality + +extension BitSet: Equatable {} + +public func == (lhs: BitSet, rhs: BitSet) -> Bool { + return lhs.words == rhs.words +} + +// MARK: - Hashing + +extension BitSet: Hashable {} + +// MARK: - Bitwise operations + +extension BitSet { + public static var allZeros: BitSet { + return BitSet(size: 64) + } +} + +private func copyLargest(_ lhs: BitSet, _ rhs: BitSet) -> BitSet { + return (lhs.words.count > rhs.words.count) ? lhs : rhs +} + +/* + Note: In all of these bitwise operations, lhs and rhs are allowed to have a + different number of bits. The new BitSet always has the larger size. + The extra bits that get added to the smaller BitSet are considered to be 0. + That will strip off the higher bits from the larger BitSet when doing &. +*/ + +public func & (lhs: BitSet, rhs: BitSet) -> BitSet { + let m = max(lhs.size, rhs.size) + var out = BitSet(size: m) + let n = min(lhs.words.count, rhs.words.count) + for i in 0.. BitSet { + var out = copyLargest(lhs, rhs) + let n = min(lhs.words.count, rhs.words.count) + for i in 0.. BitSet { + var out = copyLargest(lhs, rhs) + let n = min(lhs.words.count, rhs.words.count) + for i in 0.. BitSet { + var out = BitSet(size: rhs.size) + for i in 0.. BitSet { + var out = lhs + let offset = numBitsLeft / lhs.N + let shift = numBitsLeft % lhs.N + for i in 0..= 0 { + out.words[i] = lhs.words[i - offset] << shift + } + if i - offset - 1 >= 0 { + out.words[i] |= lhs.words[i - offset - 1] >> (lhs.N - shift) + } + } + out.clearUnusedBits() + return out +} + +public func >> (lhs: BitSet, numBitsRight: Int) -> BitSet { + var out = lhs + let offset = numBitsRight / lhs.N + let shift = numBitsRight % lhs.N + for i in 0..> shift + } + if i + offset + 1 < lhs.words.count { + out.words[i] |= lhs.words[i + offset + 1] << (lhs.N - shift) + } + } + out.clearUnusedBits() + return out +} + +// MARK: - Debugging + +extension UInt64 { + /* Writes the bits in little-endian order, LSB first. */ + public func bitsToString() -> String { + var s = "" + var n = self + for _ in 1...64 { + s += ((n & 1 == 1) ? "1" : "0") + n >>= 1 + } + return s + } +} + +extension BitSet: CustomStringConvertible { + public var description: String { + var s = "" + for x in words { + s += x.bitsToString() + " " + } + return s + } +} diff --git a/SwiftAlgorithmClub/BitSet.playground/contents.xcplayground b/SwiftAlgorithmClub/BitSet.playground/contents.xcplayground new file mode 100644 index 000000000..69d154d1e --- /dev/null +++ b/SwiftAlgorithmClub/BitSet.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/SwiftAlgorithmClub/BitSet.playground/playground.xcworkspace/contents.xcworkspacedata b/SwiftAlgorithmClub/BitSet.playground/playground.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/SwiftAlgorithmClub/BitSet.playground/playground.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/SwiftAlgorithmClub/BitSet.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/SwiftAlgorithmClub/BitSet.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/SwiftAlgorithmClub/BitSet.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/SwiftAlgorithmClub/BloomFilter.playground/Contents.swift b/SwiftAlgorithmClub/BloomFilter.playground/Contents.swift new file mode 100644 index 000000000..034aa558e --- /dev/null +++ b/SwiftAlgorithmClub/BloomFilter.playground/Contents.swift @@ -0,0 +1,82 @@ +//: Playground - noun: a place where people can play + +public class BloomFilter { + fileprivate var array: [Bool] + private var hashFunctions: [(T) -> Int] + + public init(size: Int = 1024, hashFunctions: [(T) -> Int]) { + self.array = [Bool](repeating: false, count: size) + self.hashFunctions = hashFunctions + } + + private func computeHashes(_ value: T) -> [Int] { + return hashFunctions.map { hashFunc in abs(hashFunc(value) % array.count) } + } + + public func insert(_ element: T) { + for hashValue in computeHashes(element) { + array[hashValue] = true + } + } + + public func insert(_ values: [T]) { + for value in values { + insert(value) + } + } + + public func query(_ value: T) -> Bool { + let hashValues = computeHashes(value) + + // Map hashes to indices in the Bloom Filter + let results = hashValues.map { hashValue in array[hashValue] } + + // All values must be 'true' for the query to return true + + // This does NOT imply that the value is in the Bloom filter, + // only that it may be. If the query returns false, however, + // you can be certain that the value was not added. + + let exists = results.allSatisfy { $0 } + return exists + } + + public func isEmpty() -> Bool { + // As soon as the reduction hits a 'true' value, the && condition will fail. + array.first { !$0 } ?? true + } +} + +/* Two hash functions, adapted from http://www.cse.yorku.ca/~oz/hash.html */ + +func djb2(x: String) -> Int { + var hash = 5381 + for char in x { + hash = ((hash << 5) &+ hash) &+ char.hashValue + } + return Int(hash) +} + +func sdbm(x: String) -> Int { + var hash = 0 + for char in x { + hash = char.hashValue &+ (hash << 6) &+ (hash << 16) &- hash + } + return Int(hash) +} + +/* A simple test */ + +let bloom = BloomFilter(size: 17, hashFunctions: [djb2, sdbm]) + +bloom.insert("Hello world!") +print(bloom.array) + +bloom.query("Hello world!") // true +bloom.query("Hello WORLD") // false + +bloom.insert("Bloom Filterz") +print(bloom.array) + +bloom.query("Bloom Filterz") // true +bloom.query("Hello WORLD") // true diff --git a/SwiftAlgorithmClub/BloomFilter.playground/contents.xcplayground b/SwiftAlgorithmClub/BloomFilter.playground/contents.xcplayground new file mode 100644 index 000000000..06828af92 --- /dev/null +++ b/SwiftAlgorithmClub/BloomFilter.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/SwiftAlgorithmClub/BloomFilter.playground/playground.xcworkspace/contents.xcworkspacedata b/SwiftAlgorithmClub/BloomFilter.playground/playground.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/SwiftAlgorithmClub/BloomFilter.playground/playground.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/SwiftAlgorithmClub/BloomFilter.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/SwiftAlgorithmClub/BloomFilter.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/SwiftAlgorithmClub/BloomFilter.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/SwiftAlgorithmClub/BoundedPriorityQueue.playground/Contents.swift b/SwiftAlgorithmClub/BoundedPriorityQueue.playground/Contents.swift new file mode 100644 index 000000000..d6204abec --- /dev/null +++ b/SwiftAlgorithmClub/BoundedPriorityQueue.playground/Contents.swift @@ -0,0 +1,98 @@ +struct Message: Comparable, CustomStringConvertible { + let name: String + let priority: Int + + var description: String { + return "\(name):\(priority)" + } +} + +func == (m1: Message, m2: Message) -> Bool { + return m1.priority == m2.priority +} + +func < (m1: Message, m2: Message) -> Bool { + return m1.priority < m2.priority +} + +let queue = BoundedPriorityQueue(maxElements: 5) +queue.count + +queue.enqueue(Message(name: "hello", priority: 100)) +queue.count +queue.peek() +print(queue) + +queue.enqueue(Message(name: "there", priority: 99)) +queue.count +queue.peek() +print(queue) + +queue.enqueue(Message(name: "world", priority: 150)) +queue.count +queue.peek() +print(queue) + +queue.enqueue(Message(name: "swift", priority: 110)) +queue.count +queue.peek() +print(queue) + +queue.enqueue(Message(name: "is", priority: 30)) +queue.count +queue.peek() +print(queue) + +// At this point, the queue is: +// + +// Try to insert an item with a really low priority. This should not get added. +queue.enqueue(Message(name: "very", priority: -1)) +queue.count // 5 +queue.peek() +print(queue) // still same as before + +// Try to insert an item with medium priority. This gets added and the lowest +// priority item is removed. +queue.enqueue(Message(name: "cool", priority: 120)) +queue.count +queue.peek() +print(queue) + +// Try to insert an item with very high priority. This gets added and the +// lowest priority item is removed. +queue.enqueue(Message(name: "!!!", priority: 500)) +queue.count +queue.peek() +print(queue) + +// Test dequeuing +queue.dequeue() +queue.count +queue.peek() +print(queue) + +queue.dequeue() +queue.count +queue.peek() +print(queue) + +queue.dequeue() +queue.count +queue.peek() +print(queue) + +queue.dequeue() +queue.count +queue.peek() +print(queue) + +queue.dequeue() +queue.count +queue.peek() +print(queue) + +queue.dequeue() +queue.count +queue.peek() +print(queue) diff --git a/SwiftAlgorithmClub/BoundedPriorityQueue.playground/Sources/BoundedPriorityQueue.swift b/SwiftAlgorithmClub/BoundedPriorityQueue.playground/Sources/BoundedPriorityQueue.swift new file mode 100644 index 000000000..cf0bb4e95 --- /dev/null +++ b/SwiftAlgorithmClub/BoundedPriorityQueue.playground/Sources/BoundedPriorityQueue.swift @@ -0,0 +1,137 @@ +public class LinkedListNode { + var value: T + var next: LinkedListNode? + var previous: LinkedListNode? + + public init(value: T) { + self.value = value + } +} + +public class BoundedPriorityQueue { + fileprivate typealias Node = LinkedListNode + + private(set) public var count = 0 + fileprivate var head: Node? + private var tail: Node? + private var maxElements: Int + + public init(maxElements: Int) { + self.maxElements = maxElements + } + + public var isEmpty: Bool { + return count == 0 + } + + public func peek() -> T? { + return head?.value + } + + public func enqueue(_ value: T) { + if let node = insert(value, after: findInsertionPoint(value)) { + // If the newly inserted node is the last one in the list, then update + // the tail pointer. + if node.next == nil { + tail = node + } + + // If the queue is full, then remove an element from the back. + count += 1 + if count > maxElements { + removeLeastImportantElement() + } + } + } + + private func insert(_ value: T, after: Node?) -> Node? { + if let previous = after { + + // If the queue is full and we have to insert at the end of the list, + // then there's no reason to insert the new value. + if count == maxElements && previous.next == nil { + print("Queue is full and priority of new object is too small") + return nil + } + + // Put the new node in between previous and previous.next (if exists). + let node = Node(value: value) + node.next = previous.next + previous.next?.previous = node + previous.next = node + node.previous = previous + return node + + } else if let first = head { + // Have to insert at the head, so shift the existing head up once place. + head = Node(value: value) + head!.next = first + first.previous = head + return head + + } else { + // This is the very first item in the queue. + head = Node(value: value) + return head + } + } + + /* Find the node after which to insert the new value. If this returns nil, + the new value should be inserted at the head of the list. */ + private func findInsertionPoint(_ value: T) -> Node? { + var node = head + var prev: Node? + + while let current = node, value < current.value { + prev = node + node = current.next + } + return prev + } + + private func removeLeastImportantElement() { + if let last = tail { + tail = last.previous + tail?.next = nil + count -= 1 + } + + // Note: Instead of using a tail pointer, we could just scan from the new + // node until the end. Then nodes also don't need a previous pointer. But + // this is much slower on large lists. + } + + public func dequeue() -> T? { + if let first = head { + count -= 1 + if count == 0 { + head = nil + tail = nil + } else { + head = first.next + head!.previous = nil + } + return first.value + } else { + return nil + } + } +} + +extension LinkedListNode: CustomStringConvertible { + public var description: String { + return "\(value)" + } +} + +extension BoundedPriorityQueue: CustomStringConvertible { + public var description: String { + var s = "<" + var node = head + while let current = node { + s += "\(current), " + node = current.next + } + return s + ">" + } +} diff --git a/SwiftAlgorithmClub/BoundedPriorityQueue.playground/contents.xcplayground b/SwiftAlgorithmClub/BoundedPriorityQueue.playground/contents.xcplayground new file mode 100644 index 000000000..69d154d1e --- /dev/null +++ b/SwiftAlgorithmClub/BoundedPriorityQueue.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/SwiftAlgorithmClub/BoundedPriorityQueue.playground/playground.xcworkspace/contents.xcworkspacedata b/SwiftAlgorithmClub/BoundedPriorityQueue.playground/playground.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/SwiftAlgorithmClub/BoundedPriorityQueue.playground/playground.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/SwiftAlgorithmClub/BoundedPriorityQueue.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/SwiftAlgorithmClub/BoundedPriorityQueue.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/SwiftAlgorithmClub/BoundedPriorityQueue.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/SwiftAlgorithmClub/BoyerMooreHorspool.playground/Contents.swift b/SwiftAlgorithmClub/BoyerMooreHorspool.playground/Contents.swift new file mode 100644 index 000000000..933cbc4ac --- /dev/null +++ b/SwiftAlgorithmClub/BoyerMooreHorspool.playground/Contents.swift @@ -0,0 +1,88 @@ +//: Playground - noun: a place where people can play + +/* + Boyer-Moore string search + + This code is based on the article "Faster String Searches" by Costas Menico + from Dr Dobb's magazine, July 1989. + http://www.drdobbs.com/database/faster-string-searches/184408171 +*/ +extension String { + func index(of pattern: String, usingHorspoolImprovement: Bool = false) -> Index? { + // Cache the length of the search pattern because we're going to + // use it a few times and it's expensive to calculate. + let patternLength = pattern.count + guard patternLength > 0, patternLength <= self.count else { return nil } + + // Make the skip table. This table determines how far we skip ahead + // when a character from the pattern is found. + var skipTable = [Character: Int]() + for (i, c) in pattern.enumerated() { + skipTable[c] = patternLength - i - 1 + } + + // This points at the last character in the pattern. + let p = pattern.index(before: pattern.endIndex) + let lastChar = pattern[p] + + // The pattern is scanned right-to-left, so skip ahead in the string by + // the length of the pattern. (Minus 1 because startIndex already points + // at the first character in the source string.) + var i = index(startIndex, offsetBy: patternLength - 1) + + // This is a helper function that steps backwards through both strings + // until we find a character that doesn’t match, or until we’ve reached + // the beginning of the pattern. + func backwards() -> Index? { + var q = p + var j = i + while q > pattern.startIndex { + j = index(before: j) + q = index(before: q) + if self[j] != pattern[q] { return nil } + } + return j + } + + // The main loop. Keep going until the end of the string is reached. + while i < endIndex { + let c = self[i] + + // Does the current character match the last character from the pattern? + if c == lastChar { + + // There is a possible match. Do a brute-force search backwards. + if let k = backwards() { return k } + + if !usingHorspoolImprovement { + // If no match, we can only safely skip one character ahead. + i = index(after: i) + } else { + // Ensure to jump at least one character (this is needed because the first + // character is in the skipTable, and `skipTable[lastChar] = 0`) + let jumpOffset = max(skipTable[c] ?? patternLength, 1) + i = index(i, offsetBy: jumpOffset, limitedBy: endIndex) ?? endIndex + } + } else { + // The characters are not equal, so skip ahead. The amount to skip is + // determined by the skip table. If the character is not present in the + // pattern, we can skip ahead by the full pattern length. However, if + // the character *is* present in the pattern, there may be a match up + // ahead and we can't skip as far. + i = index(i, offsetBy: skipTable[c] ?? patternLength, limitedBy: endIndex) ?? endIndex + } + } + return nil + } +} + +// A few simple tests + +let str = "Hello, World" +str.index(of: "World") // 7 + +let animals = "🐶🐔🐷🐮🐱" +animals.index(of: "🐮") // 6 + +let lorem = "Lorem ipsum dolor sit amet" +lorem.index(of: "sit", usingHorspoolImprovement: true) // 18 diff --git a/SwiftAlgorithmClub/BoyerMooreHorspool.playground/contents.xcplayground b/SwiftAlgorithmClub/BoyerMooreHorspool.playground/contents.xcplayground new file mode 100644 index 000000000..06828af92 --- /dev/null +++ b/SwiftAlgorithmClub/BoyerMooreHorspool.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/SwiftAlgorithmClub/BoyerMooreHorspool.playground/playground.xcworkspace/contents.xcworkspacedata b/SwiftAlgorithmClub/BoyerMooreHorspool.playground/playground.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/SwiftAlgorithmClub/BoyerMooreHorspool.playground/playground.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/SwiftAlgorithmClub/BoyerMooreHorspool.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/SwiftAlgorithmClub/BoyerMooreHorspool.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/SwiftAlgorithmClub/BoyerMooreHorspool.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/SwiftAlgorithmClub/BoyerMooreHorspool.playground/timeline.xctimeline b/SwiftAlgorithmClub/BoyerMooreHorspool.playground/timeline.xctimeline new file mode 100644 index 000000000..2688d72c1 --- /dev/null +++ b/SwiftAlgorithmClub/BoyerMooreHorspool.playground/timeline.xctimeline @@ -0,0 +1,21 @@ + + + + + + + + + + + diff --git a/SwiftAlgorithmClub/BreadthFirstSearch.playground/Pages/Simple example.xcplaygroundpage/Contents.swift b/SwiftAlgorithmClub/BreadthFirstSearch.playground/Pages/Simple example.xcplaygroundpage/Contents.swift new file mode 100644 index 000000000..675be2203 --- /dev/null +++ b/SwiftAlgorithmClub/BreadthFirstSearch.playground/Pages/Simple example.xcplaygroundpage/Contents.swift @@ -0,0 +1,44 @@ +func breadthFirstSearch(_ graph: Graph, source: Node) -> [String] { + var queue = Queue() + queue.enqueue(source) + + var nodesExplored = [source.label] + source.visited = true + + while let node = queue.dequeue() { + for edge in node.neighbors { + let neighborNode = edge.neighbor + if !neighborNode.visited { + queue.enqueue(neighborNode) + neighborNode.visited = true + nodesExplored.append(neighborNode.label) + } + } + } + + return nodesExplored +} + +let graph = Graph() + +let nodeA = graph.addNode("a") +let nodeB = graph.addNode("b") +let nodeC = graph.addNode("c") +let nodeD = graph.addNode("d") +let nodeE = graph.addNode("e") +let nodeF = graph.addNode("f") +let nodeG = graph.addNode("g") +let nodeH = graph.addNode("h") + +graph.addEdge(nodeA, neighbor: nodeB) +graph.addEdge(nodeA, neighbor: nodeC) +graph.addEdge(nodeB, neighbor: nodeD) +graph.addEdge(nodeB, neighbor: nodeE) +graph.addEdge(nodeC, neighbor: nodeF) +graph.addEdge(nodeC, neighbor: nodeG) +graph.addEdge(nodeE, neighbor: nodeH) +graph.addEdge(nodeE, neighbor: nodeF) +graph.addEdge(nodeF, neighbor: nodeG) + +let nodesExplored = breadthFirstSearch(graph, source: nodeA) +print(nodesExplored) diff --git a/SwiftAlgorithmClub/BreadthFirstSearch.playground/Sources/Edge.swift b/SwiftAlgorithmClub/BreadthFirstSearch.playground/Sources/Edge.swift new file mode 100644 index 000000000..7c841be30 --- /dev/null +++ b/SwiftAlgorithmClub/BreadthFirstSearch.playground/Sources/Edge.swift @@ -0,0 +1,11 @@ +public class Edge: Equatable { + public var neighbor: Node + + public init(_ neighbor: Node) { + self.neighbor = neighbor + } +} + +public func == (_ lhs: Edge, rhs: Edge) -> Bool { + return lhs.neighbor == rhs.neighbor +} diff --git a/SwiftAlgorithmClub/BreadthFirstSearch.playground/Sources/Graph.swift b/SwiftAlgorithmClub/BreadthFirstSearch.playground/Sources/Graph.swift new file mode 100644 index 000000000..0f47786ae --- /dev/null +++ b/SwiftAlgorithmClub/BreadthFirstSearch.playground/Sources/Graph.swift @@ -0,0 +1,53 @@ +public class Graph: CustomStringConvertible, Equatable { + public private(set) var nodes: [Node] + + public init() { + self.nodes = [] + } + + @discardableResult public func addNode(_ label: String) -> Node { + let node = Node(label) + nodes.append(node) + return node + } + + public func addEdge(_ source: Node, neighbor: Node) { + let edge = Edge(neighbor) + source.neighbors.append(edge) + } + + public var description: String { + var description = "" + + for node in nodes where !node.neighbors.isEmpty { + description += "[node: \(node.label) edges: \(node.neighbors.map { $0.neighbor.label})]" + } + return description + } + + public func findNodeWithLabel(_ label: String) -> Node { + return nodes.filter { $0.label == label }.first! + } + + public func duplicate() -> Graph { + let duplicated = Graph() + + for node in nodes { + duplicated.addNode(node.label) + } + + for node in nodes { + for edge in node.neighbors { + let source = duplicated.findNodeWithLabel(node.label) + let neighbour = duplicated.findNodeWithLabel(edge.neighbor.label) + duplicated.addEdge(source, neighbor: neighbour) + } + } + + return duplicated + } +} + +public func == (_ lhs: Graph, rhs: Graph) -> Bool { + return lhs.nodes == rhs.nodes +} diff --git a/SwiftAlgorithmClub/BreadthFirstSearch.playground/Sources/Node.swift b/SwiftAlgorithmClub/BreadthFirstSearch.playground/Sources/Node.swift new file mode 100644 index 000000000..6020bd596 --- /dev/null +++ b/SwiftAlgorithmClub/BreadthFirstSearch.playground/Sources/Node.swift @@ -0,0 +1,32 @@ +public class Node: CustomStringConvertible, Equatable { + public var neighbors: [Edge] + + public private(set) var label: String + public var distance: Int? + public var visited: Bool + + public init(_ label: String) { + self.label = label + neighbors = [] + visited = false + } + + public var description: String { + if let distance = distance { + return "Node(label: \(label), distance: \(distance))" + } + return "Node(label: \(label), distance: infinity)" + } + + public var hasDistance: Bool { + return distance != nil + } + + public func remove(_ edge: Edge) { + neighbors.remove(at: neighbors.firstIndex { $0 === edge }!) + } +} + +public func == (_ lhs: Node, rhs: Node) -> Bool { + return lhs.label == rhs.label && lhs.neighbors == rhs.neighbors +} diff --git a/SwiftAlgorithmClub/BreadthFirstSearch.playground/Sources/Queue.swift b/SwiftAlgorithmClub/BreadthFirstSearch.playground/Sources/Queue.swift new file mode 100644 index 000000000..3d98f801c --- /dev/null +++ b/SwiftAlgorithmClub/BreadthFirstSearch.playground/Sources/Queue.swift @@ -0,0 +1,31 @@ +public struct Queue { + private var array: [T] + + public init() { + array = [] + } + + public var isEmpty: Bool { + return array.isEmpty + } + + public var count: Int { + return array.count + } + + public mutating func enqueue(_ element: T) { + array.append(element) + } + + public mutating func dequeue() -> T? { + if isEmpty { + return nil + } else { + return array.removeFirst() + } + } + + public func peek() -> T? { + return array.first + } +} diff --git a/SwiftAlgorithmClub/BreadthFirstSearch.playground/contents.xcplayground b/SwiftAlgorithmClub/BreadthFirstSearch.playground/contents.xcplayground new file mode 100644 index 000000000..f635e9804 --- /dev/null +++ b/SwiftAlgorithmClub/BreadthFirstSearch.playground/contents.xcplayground @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/SwiftAlgorithmClub/BreadthFirstSearch.playground/playground.xcworkspace/contents.xcworkspacedata b/SwiftAlgorithmClub/BreadthFirstSearch.playground/playground.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/SwiftAlgorithmClub/BreadthFirstSearch.playground/playground.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/SwiftAlgorithmClub/BreadthFirstSearch.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/SwiftAlgorithmClub/BreadthFirstSearch.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/SwiftAlgorithmClub/BreadthFirstSearch.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/SwiftAlgorithmClub/BruteForceStringSearch.playground/Contents.swift b/SwiftAlgorithmClub/BruteForceStringSearch.playground/Contents.swift new file mode 100644 index 000000000..d48c999cc --- /dev/null +++ b/SwiftAlgorithmClub/BruteForceStringSearch.playground/Contents.swift @@ -0,0 +1,29 @@ +extension String { + func indexOf(_ pattern: String) -> String.Index? { + + for i in self.characters.indices { + var j = i + var found = true + for p in pattern.characters.indices { + if j == self.characters.endIndex || self[j] != pattern[p] { + found = false + break + } else { + j = self.characters.index(after: j) + } + } + if found { + return i + } + } + return nil + } +} + +// A few simple tests + +let s = "Hello, World" +s.indexOf("World") // 7 + +let animals = "🐶🐔🐷🐮🐱" +animals.indexOf("🐮") // 6 diff --git a/SwiftAlgorithmClub/BruteForceStringSearch.playground/contents.xcplayground b/SwiftAlgorithmClub/BruteForceStringSearch.playground/contents.xcplayground new file mode 100644 index 000000000..06828af92 --- /dev/null +++ b/SwiftAlgorithmClub/BruteForceStringSearch.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/SwiftAlgorithmClub/BruteForceStringSearch.playground/playground.xcworkspace/contents.xcworkspacedata b/SwiftAlgorithmClub/BruteForceStringSearch.playground/playground.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/SwiftAlgorithmClub/BruteForceStringSearch.playground/playground.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/SwiftAlgorithmClub/Podfile b/SwiftAlgorithmClub/Podfile new file mode 100644 index 000000000..fa6dec7dd --- /dev/null +++ b/SwiftAlgorithmClub/Podfile @@ -0,0 +1,5 @@ +target 'SwiftAlgorithmClub' do + use_frameworks! + + pod 'SwiftLint' +end diff --git a/SwiftAlgorithmClub/SwiftAlgorithmClub.xcodeproj/project.pbxproj b/SwiftAlgorithmClub/SwiftAlgorithmClub.xcodeproj/project.pbxproj new file mode 100644 index 000000000..1dea77e30 --- /dev/null +++ b/SwiftAlgorithmClub/SwiftAlgorithmClub.xcodeproj/project.pbxproj @@ -0,0 +1,401 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 60908F9123691B81004E53B2 /* SwiftAlgorithmClub.h in Headers */ = {isa = PBXBuildFile; fileRef = 60908F8F23691B81004E53B2 /* SwiftAlgorithmClub.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 896EC48BF398EBA0498F3F37 /* Pods_SwiftAlgorithmClub.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4E71A86B58EDBA0CFCF6ACDC /* Pods_SwiftAlgorithmClub.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 19B6A76A8179150D7F857F86 /* Pods-SwiftAlgorithmClub.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwiftAlgorithmClub.release.xcconfig"; path = "Target Support Files/Pods-SwiftAlgorithmClub/Pods-SwiftAlgorithmClub.release.xcconfig"; sourceTree = ""; }; + 1AC49BC223347449D9B873B3 /* Pods-SwiftAlgorithmClub.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwiftAlgorithmClub.debug.xcconfig"; path = "Target Support Files/Pods-SwiftAlgorithmClub/Pods-SwiftAlgorithmClub.debug.xcconfig"; sourceTree = ""; }; + 4E71A86B58EDBA0CFCF6ACDC /* Pods_SwiftAlgorithmClub.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SwiftAlgorithmClub.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 60908F8C23691B81004E53B2 /* SwiftAlgorithmClub.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftAlgorithmClub.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 60908F8F23691B81004E53B2 /* SwiftAlgorithmClub.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SwiftAlgorithmClub.h; sourceTree = ""; }; + 60908F9023691B81004E53B2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 60908F8923691B81004E53B2 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 896EC48BF398EBA0498F3F37 /* Pods_SwiftAlgorithmClub.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 60908F8223691B81004E53B2 = { + isa = PBXGroup; + children = ( + 60908F8E23691B81004E53B2 /* SwiftAlgorithmClub */, + 60908F8D23691B81004E53B2 /* Products */, + BA921E75CF90836A6FFD54BD /* Pods */, + 76C74A433C3D40B49A759205 /* Frameworks */, + ); + sourceTree = ""; + }; + 60908F8D23691B81004E53B2 /* Products */ = { + isa = PBXGroup; + children = ( + 60908F8C23691B81004E53B2 /* SwiftAlgorithmClub.framework */, + ); + name = Products; + sourceTree = ""; + }; + 60908F8E23691B81004E53B2 /* SwiftAlgorithmClub */ = { + isa = PBXGroup; + children = ( + 60908F8F23691B81004E53B2 /* SwiftAlgorithmClub.h */, + 60908F9023691B81004E53B2 /* Info.plist */, + ); + path = SwiftAlgorithmClub; + sourceTree = ""; + }; + 76C74A433C3D40B49A759205 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 4E71A86B58EDBA0CFCF6ACDC /* Pods_SwiftAlgorithmClub.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + BA921E75CF90836A6FFD54BD /* Pods */ = { + isa = PBXGroup; + children = ( + 1AC49BC223347449D9B873B3 /* Pods-SwiftAlgorithmClub.debug.xcconfig */, + 19B6A76A8179150D7F857F86 /* Pods-SwiftAlgorithmClub.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 60908F8723691B81004E53B2 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 60908F9123691B81004E53B2 /* SwiftAlgorithmClub.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 60908F8B23691B81004E53B2 /* SwiftAlgorithmClub */ = { + isa = PBXNativeTarget; + buildConfigurationList = 60908F9423691B81004E53B2 /* Build configuration list for PBXNativeTarget "SwiftAlgorithmClub" */; + buildPhases = ( + 5B0CDFC13A7E532D8AD759CD /* [CP] Check Pods Manifest.lock */, + 60908F8723691B81004E53B2 /* Headers */, + 60908F8823691B81004E53B2 /* Sources */, + 60908F8923691B81004E53B2 /* Frameworks */, + 60908F8A23691B81004E53B2 /* Resources */, + 60B8E3912369304400940496 /* ShellScript */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SwiftAlgorithmClub; + productName = SwiftAlgorithmClub; + productReference = 60908F8C23691B81004E53B2 /* SwiftAlgorithmClub.framework */; + productType = "com.apple.product-type.framework"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 60908F8323691B81004E53B2 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1110; + ORGANIZATIONNAME = Raywenderlich; + TargetAttributes = { + 60908F8B23691B81004E53B2 = { + CreatedOnToolsVersion = 11.1; + }; + }; + }; + buildConfigurationList = 60908F8623691B81004E53B2 /* Build configuration list for PBXProject "SwiftAlgorithmClub" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 60908F8223691B81004E53B2; + productRefGroup = 60908F8D23691B81004E53B2 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 60908F8B23691B81004E53B2 /* SwiftAlgorithmClub */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 60908F8A23691B81004E53B2 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 5B0CDFC13A7E532D8AD759CD /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-SwiftAlgorithmClub-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 60B8E3912369304400940496 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "# Type a script or drag a script file from your workspace to insert its path.\n\"${PODS_ROOT}/SwiftLint/swiftlint\"\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 60908F8823691B81004E53B2 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 60908F9223691B81004E53B2 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 60908F9323691B81004E53B2 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MACOSX_DEPLOYMENT_TARGET = 10.15; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 60908F9523691B81004E53B2 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 1AC49BC223347449D9B873B3 /* Pods-SwiftAlgorithmClub.debug.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = 99A58D6B2S; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = SwiftAlgorithmClub/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.raywenderlich.SwiftAlgorithmClub; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + }; + name = Debug; + }; + 60908F9623691B81004E53B2 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 19B6A76A8179150D7F857F86 /* Pods-SwiftAlgorithmClub.release.xcconfig */; + buildSettings = { + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + DEFINES_MODULE = YES; + DEVELOPMENT_TEAM = 99A58D6B2S; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = SwiftAlgorithmClub/Info.plist; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = com.raywenderlich.SwiftAlgorithmClub; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SKIP_INSTALL = YES; + SWIFT_VERSION = 5.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 60908F8623691B81004E53B2 /* Build configuration list for PBXProject "SwiftAlgorithmClub" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 60908F9223691B81004E53B2 /* Debug */, + 60908F9323691B81004E53B2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 60908F9423691B81004E53B2 /* Build configuration list for PBXNativeTarget "SwiftAlgorithmClub" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 60908F9523691B81004E53B2 /* Debug */, + 60908F9623691B81004E53B2 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 60908F8323691B81004E53B2 /* Project object */; +} diff --git a/SwiftAlgorithmClub/SwiftAlgorithmClub.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/SwiftAlgorithmClub/SwiftAlgorithmClub.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..38cc0b256 --- /dev/null +++ b/SwiftAlgorithmClub/SwiftAlgorithmClub.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/SwiftAlgorithmClub/SwiftAlgorithmClub.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/SwiftAlgorithmClub/SwiftAlgorithmClub.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/SwiftAlgorithmClub/SwiftAlgorithmClub.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/SwiftAlgorithmClub/SwiftAlgorithmClub.xcodeproj/xcshareddata/xcschemes/SwiftAlgorithmClub.xcscheme b/SwiftAlgorithmClub/SwiftAlgorithmClub.xcodeproj/xcshareddata/xcschemes/SwiftAlgorithmClub.xcscheme new file mode 100644 index 000000000..b46cf11cc --- /dev/null +++ b/SwiftAlgorithmClub/SwiftAlgorithmClub.xcodeproj/xcshareddata/xcschemes/SwiftAlgorithmClub.xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SwiftAlgorithmClub/SwiftAlgorithmClub.xcworkspace/contents.xcworkspacedata b/SwiftAlgorithmClub/SwiftAlgorithmClub.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..1488a9d4b --- /dev/null +++ b/SwiftAlgorithmClub/SwiftAlgorithmClub.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SwiftAlgorithmClub/SwiftAlgorithmClub.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/SwiftAlgorithmClub/SwiftAlgorithmClub.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/SwiftAlgorithmClub/SwiftAlgorithmClub.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/SwiftAlgorithmClub/SwiftAlgorithmClub/Info.plist b/SwiftAlgorithmClub/SwiftAlgorithmClub/Info.plist new file mode 100644 index 000000000..5f3259c30 --- /dev/null +++ b/SwiftAlgorithmClub/SwiftAlgorithmClub/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + $(CURRENT_PROJECT_VERSION) + NSHumanReadableCopyright + Copyright © 2019 Raywenderlich. All rights reserved. + + diff --git a/SwiftAlgorithmClub/SwiftAlgorithmClub/SwiftAlgorithmClub.h b/SwiftAlgorithmClub/SwiftAlgorithmClub/SwiftAlgorithmClub.h new file mode 100644 index 000000000..fda7bae90 --- /dev/null +++ b/SwiftAlgorithmClub/SwiftAlgorithmClub/SwiftAlgorithmClub.h @@ -0,0 +1,19 @@ +// +// SwiftAlgorithmClub.h +// SwiftAlgorithmClub +// +// Created by Kelvin Lau on 2019-10-29. +// Copyright © 2019 Raywenderlich. All rights reserved. +// + +#import + +//! Project version number for SwiftAlgorithmClub. +FOUNDATION_EXPORT double SwiftAlgorithmClubVersionNumber; + +//! Project version string for SwiftAlgorithmClub. +FOUNDATION_EXPORT const unsigned char SwiftAlgorithmClubVersionString[]; + +// In this header, you should import all the public headers of your framework using statements like #import + +