diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index e5d00c17b..29f4ca7e1 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -6,7 +6,7 @@ Want to help out with the Swift Algorithm Club? Great! While we don't have stric Our repo is all about learning. The `README` file is the cake, and the sample code is the cherry on top. A good contribution has succinct explanations supported by diagrams. Code is best introduced in chunks, weaved into the explanations where relevant. -> When choosing between brievity and performance, err to the side of brievity as long as the time complexity of the particular implementation is the same. You can make a note afterwards suggesting a more performant way of doing things. +> When choosing between brevity and performance, err to the side of brevity as long as the time complexity of the particular implementation is the same. You can make a note afterwards suggesting a more performant way of doing things. **API Design Guidelines** @@ -22,7 +22,7 @@ We follow the following Swift [style guide](https://github.com/raywenderlich/swi Unit tests. Fixes for typos. No contribution is too small. :-) -The repository has over 100 different data structures and algorithms. We're always interested in improvements to existing implementations and better explanations. Suggestions for making the code more Swift-like or to make it fit better with the standard library is most welcomed. +The repository has over 100 different data structures and algorithms. We're always interested in improvements to existing implementations and better explanations. Suggestions for making the code more Swift-like or to make it fit better with the standard library are most welcome. ### New Contributions diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 5d2a05848..000000000 --- a/.travis.yml +++ /dev/null @@ -1,49 +0,0 @@ -language: objective-c -osx_image: xcode9 -# sudo: false - -install: - -# - ./install_swiftlint.sh - -script: - -- xcodebuild test -project ./All-Pairs\ Shortest\ Paths/APSP/APSP.xcodeproj -scheme APSPTests -- xcodebuild test -project ./Array2D/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./AVL\ Tree/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./Binary\ Search/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./Boyer-Moore-Horspool/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./Binary\ Search\ Tree/Solution\ 1/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./Bloom\ Filter/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./Bounded\ Priority\ Queue/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./Breadth-First\ Search/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./Bucket\ Sort/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./B-Tree/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./Comb\ Sort/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./Convex\ Hull/Convex\ Hull.xcodeproj -scheme Tests -destination 'platform=iOS Simulator,name=iPhone 7,OS=11.0' -- xcodebuild test -project ./Counting\ Sort/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./Depth-First\ Search/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./Graph/Graph.xcodeproj -scheme GraphTests -- xcodebuild test -project ./Heap/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./Heap\ Sort/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./Insertion\ Sort/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./K-Means/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./Linked\ List/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./Singly\ Linked\ List/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./Longest\ Common\ Subsequence/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./Minimum\ Spanning\ Tree\ \(Unweighted\)/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./Priority\ Queue/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./Queue/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./Quicksort/Tests/Tests-Quicksort.xcodeproj -scheme Tests-Quicksort -- xcodebuild test -project ./Radix\ Sort/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./Rootish\ Array\ Stack/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./Select\ Minimum\ Maximum/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./Selection\ Sort/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./Shell\ Sort/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./Shortest\ Path\ \(Unweighted\)/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./Single-Source\ Shortest\ Paths\ \(Weighted\)/SSSP.xcodeproj -scheme SSSPTests -- xcodebuild test -project ./Stack/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./Topological\ Sort/Tests/Tests.xcodeproj -scheme Tests -- xcodebuild test -project ./Treap/Treap/Treap.xcodeproj -scheme Tests -- xcodebuild test -project ./Palindromes/Test/Test.xcodeproj -scheme Test -- xcodebuild test -project ./Ternary\ Search\ Tree/Tests/Tests.xcodeproj -scheme Tests diff --git a/3Sum and 4Sum/3Sum.playground/Contents.swift b/3Sum and 4Sum/3Sum.playground/Contents.swift index 2581da6e7..d172aed1f 100644 --- a/3Sum and 4Sum/3Sum.playground/Contents.swift +++ b/3Sum and 4Sum/3Sum.playground/Contents.swift @@ -1,3 +1,7 @@ +// last checked with Xcode 10.1 +#if swift(>=4.2) +print("Hello, Swift 4.2!") +#endif extension Collection where Element: Equatable { diff --git a/Binary Search/BinarySearch.playground/playground.xcworkspace/contents.xcworkspacedata b/3Sum and 4Sum/3Sum.playground/playground.xcworkspace/contents.xcworkspacedata similarity index 100% rename from Binary Search/BinarySearch.playground/playground.xcworkspace/contents.xcworkspacedata rename to 3Sum and 4Sum/3Sum.playground/playground.xcworkspace/contents.xcworkspacedata diff --git a/3Sum and 4Sum/3Sum.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/3Sum and 4Sum/3Sum.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/3Sum and 4Sum/3Sum.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/3Sum and 4Sum/4Sum.playground/Contents.swift b/3Sum and 4Sum/4Sum.playground/Contents.swift index 86b530224..37f75918c 100644 --- a/3Sum and 4Sum/4Sum.playground/Contents.swift +++ b/3Sum and 4Sum/4Sum.playground/Contents.swift @@ -1,3 +1,8 @@ +// last checked with Xcode 10.1 +#if swift(>=4.2) +print("Hello, Swift 4.2!") +#endif + extension Collection where Element: Equatable { /// In a sorted collection, replaces the given index with a successor mapping to a unique element. diff --git a/Fizz Buzz/FizzBuzz.playground/playground.xcworkspace/contents.xcworkspacedata b/3Sum and 4Sum/4Sum.playground/playground.xcworkspace/contents.xcworkspacedata similarity index 100% rename from Fizz Buzz/FizzBuzz.playground/playground.xcworkspace/contents.xcworkspacedata rename to 3Sum and 4Sum/4Sum.playground/playground.xcworkspace/contents.xcworkspacedata diff --git a/3Sum and 4Sum/4Sum.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/3Sum and 4Sum/4Sum.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/3Sum and 4Sum/4Sum.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/A-Star/AStar.swift b/A-Star/AStar.swift new file mode 100644 index 000000000..41a9fac6c --- /dev/null +++ b/A-Star/AStar.swift @@ -0,0 +1,153 @@ +// Written by Alejandro Isaza. + +import Foundation + +public protocol Graph { + associatedtype Vertex: Hashable + associatedtype Edge: WeightedEdge where Edge.Vertex == Vertex + + /// Lists all edges going out from a vertex. + func edgesOutgoing(from vertex: Vertex) -> [Edge] +} + +public protocol WeightedEdge { + associatedtype Vertex + + /// The edge's cost. + var cost: Double { get } + + /// The target vertex. + var target: Vertex { get } +} + +public final class AStar { + /// The graph to search on. + public let graph: G + + /// The heuristic cost function that estimates the cost between two vertices. + /// + /// - Note: The heuristic function needs to always return a value that is lower-than or equal to the actual + /// cost for the resulting path of the A* search to be optimal. + public let heuristic: (G.Vertex, G.Vertex) -> Double + + /// Open list of nodes to expand. + private var open: HashedHeap> + + /// Closed list of vertices already expanded. + private var closed = Set() + + /// Actual vertex cost for vertices we already encountered (refered to as `g` on the literature). + private var costs = Dictionary() + + /// Store the previous node for each expanded node to recreate the path. + private var parents = Dictionary() + + /// Initializes `AStar` with a graph and a heuristic cost function. + public init(graph: G, heuristic: @escaping (G.Vertex, G.Vertex) -> Double) { + self.graph = graph + self.heuristic = heuristic + open = HashedHeap(sort: <) + } + + /// Finds an optimal path between `source` and `target`. + /// + /// - Precondition: both `source` and `target` belong to `graph`. + public func path(start: G.Vertex, target: G.Vertex) -> [G.Vertex] { + open.insert(Node(vertex: start, cost: 0, estimate: heuristic(start, target))) + while !open.isEmpty { + guard let node = open.remove() else { + break + } + costs[node.vertex] = node.cost + + if (node.vertex == target) { + let path = buildPath(start: start, target: target) + cleanup() + return path + } + + if !closed.contains(node.vertex) { + expand(node: node, target: target) + closed.insert(node.vertex) + } + } + + // No path found + return [] + } + + private func expand(node: Node, target: G.Vertex) { + let edges = graph.edgesOutgoing(from: node.vertex) + for edge in edges { + let g = cost(node.vertex) + edge.cost + if g < cost(edge.target) { + open.insert(Node(vertex: edge.target, cost: g, estimate: heuristic(edge.target, target))) + parents[edge.target] = node.vertex + } + } + } + + private func cost(_ vertex: G.Edge.Vertex) -> Double { + if let c = costs[vertex] { + return c + } + + let node = Node(vertex: vertex, cost: Double.greatestFiniteMagnitude, estimate: 0) + if let index = open.index(of: node) { + return open[index].cost + } + + return Double.greatestFiniteMagnitude + } + + private func buildPath(start: G.Vertex, target: G.Vertex) -> [G.Vertex] { + var path = Array() + path.append(target) + + var current = target + while current != start { + guard let parent = parents[current] else { + return [] // no path found + } + current = parent + path.append(current) + } + + return path.reversed() + } + + private func cleanup() { + open.removeAll() + closed.removeAll() + parents.removeAll() + } +} + +private struct Node: Hashable, Comparable { + /// The graph vertex. + var vertex: V + + /// The actual cost between the start vertex and this vertex. + var cost: Double + + /// Estimated (heuristic) cost betweent this vertex and the target vertex. + var estimate: Double + + public init(vertex: V, cost: Double, estimate: Double) { + self.vertex = vertex + self.cost = cost + self.estimate = estimate + } + + static func < (lhs: Node, rhs: Node) -> Bool { + return lhs.cost + lhs.estimate < rhs.cost + rhs.estimate + } + + static func == (lhs: Node, rhs: Node) -> Bool { + return lhs.vertex == rhs.vertex + } + + var hashValue: Int { + return vertex.hashValue + } +} diff --git a/A-Star/Images/graph.dot b/A-Star/Images/graph.dot new file mode 100644 index 000000000..941d66232 --- /dev/null +++ b/A-Star/Images/graph.dot @@ -0,0 +1,12 @@ +digraph G { + rankdir=LR; + { A [ label = "h = 3" ] } + { rank = same; B [ label = "h = 2" ]; C [ label = "h = 2" ]; D [ label = "h = 2" ] } + { rank = same; E [ label = "h = 1" ]; F [ label = "h = 1" ]; G [ label = "h = 1" ] } + { H [ label = "h = 0", style = filled, color = green ] } + A -> { B C D } + B -> E + E -> F + D -> G + G -> H +} diff --git a/A-Star/Images/graph.png b/A-Star/Images/graph.png new file mode 100644 index 000000000..c0a4b1cc8 Binary files /dev/null and b/A-Star/Images/graph.png differ diff --git a/A-Star/Images/step1.dot b/A-Star/Images/step1.dot new file mode 100644 index 000000000..5785aea4c --- /dev/null +++ b/A-Star/Images/step1.dot @@ -0,0 +1,12 @@ +digraph G { + rankdir=LR; + { A [ label = "g = 0\nh = 3", style = filled, color = deepskyblue1 ] } + { rank = same; B [ label = "g = 1\nh = 2", style = filled, color = lightgrey ]; C [ label = "g = 1\nh = 2", style = filled, color = lightgrey ]; D [ label = "g = 1\nh = 2", style = filled, color = lightgrey ] } + { rank = same; E [ label = "g = \?\nh = 1" ]; F [ label = "g = \?\nh = 1" ]; G [ label = "g = \?\nh = 1" ] } + { H [ label = "g = \?\nh = 0" ] } + A -> { B C D } + B -> E + E -> F + D -> G + G -> H +} diff --git a/A-Star/Images/step1.png b/A-Star/Images/step1.png new file mode 100644 index 000000000..a983033a2 Binary files /dev/null and b/A-Star/Images/step1.png differ diff --git a/A-Star/Images/step2.dot b/A-Star/Images/step2.dot new file mode 100644 index 000000000..0c8a9d53d --- /dev/null +++ b/A-Star/Images/step2.dot @@ -0,0 +1,12 @@ +digraph G { + rankdir=LR; + { A [ label = "g = 0\nh = 3", style = filled, color = lightblue ] } + { rank = same; B [ label = "g = 1\nh = 2", style = filled, color = deepskyblue1 ]; C [ label = "g = 1\nh = 2", style = filled, color = lightgrey ]; D [ label = "g = 1\nh = 2", style = filled, color = lightgrey ] } + { rank = same; E [ label = "g = 2\nh = 1", style = filled, color = lightgrey ]; F [ label = "g = \?\nh = 1" ]; G [ label = "g = \?\nh = 1" ] } + { H [ label = "g = \?\nh = 0" ] } + A -> { B C D } + B -> E + E -> F + D -> G + G -> H +} diff --git a/A-Star/Images/step2.png b/A-Star/Images/step2.png new file mode 100644 index 000000000..10b4300f4 Binary files /dev/null and b/A-Star/Images/step2.png differ diff --git a/A-Star/Images/step3.dot b/A-Star/Images/step3.dot new file mode 100644 index 000000000..32a75891d --- /dev/null +++ b/A-Star/Images/step3.dot @@ -0,0 +1,12 @@ +digraph G { + rankdir=LR; + { A [ label = "g = 0\nh = 3", style = filled, color = lightblue ] } + { rank = same; B [ label = "g = 1\nh = 2", style = filled, color = lightblue ]; C [ label = "g = 1\nh = 2", style = filled, color = deepskyblue1 ]; D [ label = "g = 1\nh = 2", style = filled, color = lightgrey ] } + { rank = same; E [ label = "g = 2\nh = 1", style = filled, color = lightgrey ]; F [ label = "g = \?\nh = 1" ]; G [ label = "g = \?\nh = 1" ] } + { H [ label = "g = \?\nh = 0" ] } + A -> { B C D } + B -> E + E -> F + D -> G + G -> H +} diff --git a/A-Star/Images/step3.png b/A-Star/Images/step3.png new file mode 100644 index 000000000..e195e7e8e Binary files /dev/null and b/A-Star/Images/step3.png differ diff --git a/A-Star/Images/step4.dot b/A-Star/Images/step4.dot new file mode 100644 index 000000000..16db76796 --- /dev/null +++ b/A-Star/Images/step4.dot @@ -0,0 +1,12 @@ +digraph G { + rankdir=LR; + { A [ label = "g = 0\nh = 3", style = filled, color = lightblue ] } + { rank = same; B [ label = "g = 1\nh = 2", style = filled, color = lightblue ]; C [ label = "g = 1\nh = 2", style = filled, color = lightblue ]; D [ label = "g = 1\nh = 2", style = filled, color = deepskyblue1 ] } + { rank = same; E [ label = "g = 2\nh = 1", style = filled, color = lightgrey ]; F [ label = "g = \?\nh = 1" ]; G [ label = "g = 2\nh = 1", style = filled, color = lightgrey ] } + { H [ label = "g = \?\nh = 0" ] } + A -> { B C D } + B -> E + E -> F + D -> G + G -> H +} diff --git a/A-Star/Images/step4.png b/A-Star/Images/step4.png new file mode 100644 index 000000000..c07f34c80 Binary files /dev/null and b/A-Star/Images/step4.png differ diff --git a/A-Star/Images/step5.dot b/A-Star/Images/step5.dot new file mode 100644 index 000000000..2986b6a90 --- /dev/null +++ b/A-Star/Images/step5.dot @@ -0,0 +1,12 @@ +digraph G { + rankdir=LR; + { A [ label = "g = 0\nh = 3", style = filled, color = lightblue ] } + { rank = same; B [ label = "g = 1\nh = 2", style = filled, color = lightblue ]; C [ label = "g = 1\nh = 2", style = filled, color = lightblue ]; D [ label = "g = 1\nh = 2", style = filled, color = lightblue ] } + { rank = same; E [ label = "g = 2\nh = 1", style = filled, color = deepskyblue1 ]; F [ label = "g = 3\nh = 1", style = filled, color = lightgrey ]; G [ label = "g = 2\nh = 1", style = filled, color = lightgrey ] } + { H [ label = "g = \?\nh = 0" ] } + A -> { B C D } + B -> E + E -> F + D -> G + G -> H +} diff --git a/A-Star/Images/step5.png b/A-Star/Images/step5.png new file mode 100644 index 000000000..40a7008da Binary files /dev/null and b/A-Star/Images/step5.png differ diff --git a/A-Star/Images/step6.dot b/A-Star/Images/step6.dot new file mode 100644 index 000000000..b5e3179b6 --- /dev/null +++ b/A-Star/Images/step6.dot @@ -0,0 +1,12 @@ +digraph G { + rankdir=LR; + { A [ label = "g = 0\nh = 3", style = filled, color = lightblue ] } + { rank = same; B [ label = "g = 1\nh = 2", style = filled, color = lightblue ]; C [ label = "g = 1\nh = 2", style = filled, color = lightblue ]; D [ label = "g = 1\nh = 2", style = filled, color = lightblue ] } + { rank = same; E [ label = "g = 2\nh = 1", style = filled, color = lightblue ]; F [ label = "g = 3\nh = 1", style = filled, color = lightgrey ]; G [ label = "g = 2\nh = 1", style = filled, color = deepskyblue1 ] } + { H [ label = "g = 3\nh = 0", style = filled, color = lightgrey ] } + A -> { B C D } + B -> E + E -> F + D -> G + G -> H +} diff --git a/A-Star/Images/step6.png b/A-Star/Images/step6.png new file mode 100644 index 000000000..9e7baef26 Binary files /dev/null and b/A-Star/Images/step6.png differ diff --git a/A-Star/Images/step7.dot b/A-Star/Images/step7.dot new file mode 100644 index 000000000..c5b26b6b3 --- /dev/null +++ b/A-Star/Images/step7.dot @@ -0,0 +1,12 @@ +digraph G { + rankdir=LR; + { A [ label = "g = 0\nh = 3", style = filled, color = lightblue ] } + { rank = same; B [ label = "g = 1\nh = 2", style = filled, color = lightblue ]; C [ label = "g = 1\nh = 2", style = filled, color = lightblue ]; D [ label = "g = 1\nh = 2", style = filled, color = lightblue ] } + { rank = same; E [ label = "g = 2\nh = 1", style = filled, color = lightblue ]; F [ label = "g = 3\nh = 1", style = filled, color = lightgrey ]; G [ label = "g = 2\nh = 1", style = filled, color = lightblue ] } + { H [ label = "g = 3\nh = 0", style = filled, color = deepskyblue1 ] } + A -> { B C D } + B -> E + E -> F + D -> G + G -> H +} diff --git a/A-Star/Images/step7.png b/A-Star/Images/step7.png new file mode 100644 index 000000000..0eedd638f Binary files /dev/null and b/A-Star/Images/step7.png differ diff --git a/A-Star/README.md b/A-Star/README.md new file mode 100644 index 000000000..b373d25fc --- /dev/null +++ b/A-Star/README.md @@ -0,0 +1,43 @@ +# A* + +A* (pronounced "ay star") is a heuristic best-first search algorithm. A* minimizes node expansions, therefore minimizing the search time, by using a heuristic function. The heuristic function gives an estimate of the distance between two vertices. For instance if you are searching for a path between two points in a city, you can estimate the actual street distance with the straight-line distance. + +A* works by expanding the most promising nodes first, according to the heuristic function. In the city example it would choose streets which go in the general direction of the target first and, only if those are dead ends, backtrack and try other streets. This speeds up search in most sitations. + +A* is optimal (it always find the shortest path) if the heuristic function is admissible. A heuristic function is admissible if it never overestimates the cost of reaching the goal. In the extreme case of the heuristic function always retuning `0` A* acts exactly the same as [Dijkstra's Algorithm](../Dijkstra). The closer the heuristic function is to the actual distance the faster the search. + +## Example + +Let's run through an example on this simple directed graph. We are going to assume that all edges have a cost of 1 and the heuristic function is going to be the column starting at goal and going back: + +![Graph](Images/graph.png) + +On the first step we expand the root node on the left (blue). We set the cost `g` to zero and add all neighbors to the open list (grey). + +![Step 1](Images/step1.png) + +We put the first node in the closed list (light blue) so that we don't try expanding it again if there were to be loops in the graph. Next we take the node on the open list with the smallest value of `g + h` where `g` is the current cost (0) plus the edge cost (1). Since all nodes in the open list have the same value we choose the top one. + +![Step 2](Images/step2.png) + +We repeat the process and pick the next node from the open list. In this case there are no new nodes to add to the open list. + +![Step 3](Images/step3.png) + +We expand the next node. One more node on the open list, but nothing exciting yet. + +![Step 4](Images/step4.png) + +Sicne the top and the bottom nodes have the same value we choose the top one. This is not a great choice but we could do better if we had a better heuristic function. + +![Step 5](Images/step5.png) + +Now we expand the bottom node because it has a smaller value than the middle node (2 + 1 < 3 + 1). + +![Step 6](Images/step6.png) + +And we finally reach the goal! We never even expanded that middle node. We didn't have to because its value is 4, which is equal to the total lenght of our solution and therefore guaranteed to not be part of the optimal solution. + +![Step 7](Images/step7.png) + +The final step is to backtrack from the goal node to buld the optimal path. diff --git a/A-Star/Tests/AStarTests.swift b/A-Star/Tests/AStarTests.swift new file mode 100755 index 000000000..87f674cd5 --- /dev/null +++ b/A-Star/Tests/AStarTests.swift @@ -0,0 +1,57 @@ +import Foundation +import XCTest + +struct GridGraph: Graph { + struct Vertex: Hashable { + var x: Int + var y: Int + + static func == (lhs: Vertex, rhs: Vertex) -> Bool { + return lhs.x == rhs.x && lhs.y == rhs.y + } + + public var hashValue: Int { + return x.hashValue ^ y.hashValue + } + } + + struct Edge: WeightedEdge { + var cost: Double + var target: Vertex + } + + func edgesOutgoing(from vertex: Vertex) -> [Edge] { + return [ + Edge(cost: 1, target: Vertex(x: vertex.x - 1, y: vertex.y)), + Edge(cost: 1, target: Vertex(x: vertex.x + 1, y: vertex.y)), + Edge(cost: 1, target: Vertex(x: vertex.x, y: vertex.y - 1)), + Edge(cost: 1, target: Vertex(x: vertex.x, y: vertex.y + 1)), + ] + } +} + +class AStarTests: XCTestCase { + func testSameStartAndEnd() { + let graph = GridGraph() + let astar = AStar(graph: graph, heuristic: manhattanDistance) + let path = astar.path(start: GridGraph.Vertex(x: 0, y: 0), target: GridGraph.Vertex(x: 0, y: 0)) + XCTAssertEqual(path.count, 1) + XCTAssertEqual(path[0].x, 0) + XCTAssertEqual(path[0].y, 0) + } + + func testDiagonal() { + let graph = GridGraph() + let astar = AStar(graph: graph, heuristic: manhattanDistance) + let path = astar.path(start: GridGraph.Vertex(x: 0, y: 0), target: GridGraph.Vertex(x: 10, y: 10)) + XCTAssertEqual(path.count, 21) + XCTAssertEqual(path[0].x, 0) + XCTAssertEqual(path[0].y, 0) + XCTAssertEqual(path[20].x, 10) + XCTAssertEqual(path[20].y, 10) + } + + func manhattanDistance(_ s: GridGraph.Vertex, _ t: GridGraph.Vertex) -> Double { + return Double(abs(s.x - t.x) + abs(s.y - t.y)) + } +} diff --git a/A-Star/Tests/AStarTests.xcodeproj/project.pbxproj b/A-Star/Tests/AStarTests.xcodeproj/project.pbxproj new file mode 100644 index 000000000..cacd1e5ae --- /dev/null +++ b/A-Star/Tests/AStarTests.xcodeproj/project.pbxproj @@ -0,0 +1,291 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXBuildFile section */ + 611D099C1F8978AB00C7092B /* AStarTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 611D099B1F8978AB00C7092B /* AStarTests.swift */; }; + 611D099E1F8978BC00C7092B /* AStar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 611D099D1F8978BB00C7092B /* AStar.swift */; }; + 611D09A01F89795100C7092B /* HashedHeap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 611D099F1F89795100C7092B /* HashedHeap.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 611D099B1F8978AB00C7092B /* AStarTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AStarTests.swift; sourceTree = ""; }; + 611D099D1F8978BB00C7092B /* AStar.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = AStar.swift; path = ../AStar.swift; sourceTree = ""; }; + 611D099F1F89795100C7092B /* HashedHeap.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = HashedHeap.swift; path = "../../Hashed Heap/HashedHeap.swift"; sourceTree = ""; }; + 7B2BBC801C779D720067B71D /* AStarTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = AStarTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 7B2BBC941C779E7B0067B71D /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = SOURCE_ROOT; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 7B2BBC7D1C779D720067B71D /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 7B2BBC681C779D710067B71D = { + isa = PBXGroup; + children = ( + 7B2BBC831C779D720067B71D /* Tests */, + 7B2BBC721C779D710067B71D /* Products */, + ); + sourceTree = ""; + }; + 7B2BBC721C779D710067B71D /* Products */ = { + isa = PBXGroup; + children = ( + 7B2BBC801C779D720067B71D /* AStarTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 7B2BBC831C779D720067B71D /* Tests */ = { + isa = PBXGroup; + children = ( + 611D099F1F89795100C7092B /* HashedHeap.swift */, + 611D099D1F8978BB00C7092B /* AStar.swift */, + 611D099B1F8978AB00C7092B /* AStarTests.swift */, + 7B2BBC941C779E7B0067B71D /* Info.plist */, + ); + name = Tests; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 7B2BBC7F1C779D720067B71D /* AStarTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7B2BBC8C1C779D720067B71D /* Build configuration list for PBXNativeTarget "AStarTests" */; + buildPhases = ( + 7B2BBC7C1C779D720067B71D /* Sources */, + 7B2BBC7D1C779D720067B71D /* Frameworks */, + 7B2BBC7E1C779D720067B71D /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = AStarTests; + productName = TestsTests; + productReference = 7B2BBC801C779D720067B71D /* AStarTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 7B2BBC691C779D710067B71D /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 0720; + LastUpgradeCheck = 0900; + ORGANIZATIONNAME = "Swift Algorithm Club"; + TargetAttributes = { + 7B2BBC7F1C779D720067B71D = { + CreatedOnToolsVersion = 7.2; + LastSwiftMigration = 0900; + }; + }; + }; + buildConfigurationList = 7B2BBC6C1C779D710067B71D /* Build configuration list for PBXProject "AStarTests" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 7B2BBC681C779D710067B71D; + productRefGroup = 7B2BBC721C779D710067B71D /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 7B2BBC7F1C779D720067B71D /* AStarTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 7B2BBC7E1C779D720067B71D /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 7B2BBC7C1C779D720067B71D /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 611D099C1F8978AB00C7092B /* AStarTests.swift in Sources */, + 611D099E1F8978BC00C7092B /* AStar.swift in Sources */, + 611D09A01F89795100C7092B /* HashedHeap.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 7B2BBC871C779D720067B71D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_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_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + 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.11; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = macosx; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + }; + name = Debug; + }; + 7B2BBC881C779D720067B71D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + 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_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_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + 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.11; + MTL_ENABLE_DEBUG_INFO = NO; + SDKROOT = macosx; + SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + }; + name = Release; + }; + 7B2BBC8D1C779D720067B71D /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = swift.algorithm.club.Tests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.0; + }; + name = Debug; + }; + 7B2BBC8E1C779D720067B71D /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_MODULES = YES; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = swift.algorithm.club.Tests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.0; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 7B2BBC6C1C779D710067B71D /* Build configuration list for PBXProject "AStarTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7B2BBC871C779D720067B71D /* Debug */, + 7B2BBC881C779D720067B71D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7B2BBC8C1C779D720067B71D /* Build configuration list for PBXNativeTarget "AStarTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7B2BBC8D1C779D720067B71D /* Debug */, + 7B2BBC8E1C779D720067B71D /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 7B2BBC691C779D710067B71D /* Project object */; +} diff --git a/A-Star/Tests/AStarTests.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/A-Star/Tests/AStarTests.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..6c0ea8493 --- /dev/null +++ b/A-Star/Tests/AStarTests.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/A-Star/Tests/AStarTests.xcodeproj/xcshareddata/xcschemes/AStarTests.xcscheme b/A-Star/Tests/AStarTests.xcodeproj/xcshareddata/xcschemes/AStarTests.xcscheme new file mode 100644 index 000000000..5473b4c0c --- /dev/null +++ b/A-Star/Tests/AStarTests.xcodeproj/xcshareddata/xcschemes/AStarTests.xcscheme @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/A-Star/Tests/Info.plist b/A-Star/Tests/Info.plist new file mode 100644 index 000000000..ba72822e8 --- /dev/null +++ b/A-Star/Tests/Info.plist @@ -0,0 +1,24 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleSignature + ???? + CFBundleVersion + 1 + + diff --git a/AVL Tree/AVLTree.playground/Contents.swift b/AVL Tree/AVLTree.playground/Contents.swift index 04ef78d07..34c1e6ddd 100644 --- a/AVL Tree/AVLTree.playground/Contents.swift +++ b/AVL Tree/AVLTree.playground/Contents.swift @@ -1,9 +1,5 @@ //: Playground - noun: a place where people can play -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif let tree = AVLTree() diff --git a/AVL Tree/README.markdown b/AVL Tree/README.markdown index e7515d99f..4e974ba71 100644 --- a/AVL Tree/README.markdown +++ b/AVL Tree/README.markdown @@ -88,4 +88,4 @@ The interesting bits are in the `balance()` method which is called after inserti AVL tree was the first self-balancing binary tree. These days, the [red-black tree](../Red-Black%20Tree/) seems to be more popular. -*Written for Swift Algorithm Club by Mike Taghavi and Matthijs Hollemans* +*Written for Swift Algorithm Club by [Mike Taghavi](https://github.com/mitghi) and [Matthijs Hollemans](https://github.com/hollance)* diff --git a/All-Pairs Shortest Paths/APSP/APSP.playground/Contents.swift b/All-Pairs Shortest Paths/APSP/APSP.playground/Contents.swift index b1b499b39..24c27cac8 100644 --- a/All-Pairs Shortest Paths/APSP/APSP.playground/Contents.swift +++ b/All-Pairs Shortest Paths/APSP/APSP.playground/Contents.swift @@ -1,10 +1,5 @@ //: Playground - noun: a place where people can play -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - import Graph import APSP diff --git a/All-Pairs Shortest Paths/APSP/APSP.playground/contents.xcplayground b/All-Pairs Shortest Paths/APSP/APSP.playground/contents.xcplayground index 06828af92..d5a8d0e3f 100644 --- a/All-Pairs Shortest Paths/APSP/APSP.playground/contents.xcplayground +++ b/All-Pairs Shortest Paths/APSP/APSP.playground/contents.xcplayground @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/All-Pairs Shortest Paths/APSP/APSP.xcodeproj/project.pbxproj b/All-Pairs Shortest Paths/APSP/APSP.xcodeproj/project.pbxproj index 78df3fb76..dc8e509a1 100644 --- a/All-Pairs Shortest Paths/APSP/APSP.xcodeproj/project.pbxproj +++ b/All-Pairs Shortest Paths/APSP/APSP.xcodeproj/project.pbxproj @@ -187,16 +187,16 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0820; + LastUpgradeCheck = 1010; ORGANIZATIONNAME = "Swift Algorithm Club"; TargetAttributes = { 493D8DDF1CDD2A1C0089795A = { CreatedOnToolsVersion = 7.3; - LastSwiftMigration = 0820; + LastSwiftMigration = 1010; }; 493D8DF01CDD5B960089795A = { CreatedOnToolsVersion = 7.3; - LastSwiftMigration = 0820; + LastSwiftMigration = 1010; }; }; }; @@ -302,14 +302,22 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = 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_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_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -349,14 +357,22 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = 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_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_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -388,7 +404,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.swift-algorithm-club.APSPTests"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; }; name = Debug; }; @@ -400,7 +416,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "com.swift-algorithm-club.APSPTests"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; }; name = Release; }; @@ -420,7 +436,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.swift-algorithm-club.APSP"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.2; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -442,7 +458,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "com.swift-algorithm-club.APSP"; PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.2; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; diff --git a/All-Pairs Shortest Paths/APSP/APSP.xcodeproj/xcshareddata/xcschemes/APSP.xcscheme b/All-Pairs Shortest Paths/APSP/APSP.xcodeproj/xcshareddata/xcschemes/APSP.xcscheme index e313c4b88..944957d8f 100644 --- a/All-Pairs Shortest Paths/APSP/APSP.xcodeproj/xcshareddata/xcschemes/APSP.xcscheme +++ b/All-Pairs Shortest Paths/APSP/APSP.xcodeproj/xcshareddata/xcschemes/APSP.xcscheme @@ -1,6 +1,6 @@ + + + + IDEDidComputeMac32BitWarning + + + diff --git a/All-Pairs Shortest Paths/APSP/APSPTests/APSPTests.swift b/All-Pairs Shortest Paths/APSP/APSPTests/APSPTests.swift index c2f64e239..ce0dfcf32 100644 --- a/All-Pairs Shortest Paths/APSP/APSPTests/APSPTests.swift +++ b/All-Pairs Shortest Paths/APSP/APSPTests/APSPTests.swift @@ -19,14 +19,6 @@ struct TestCase where T: Hashable { } class APSPTests: XCTestCase { - - func testSwift4() { - // last checked with Xcode 9.0b4 - #if swift(>=4.0) - print("Hello, Swift 4!") - #endif - } - /** See Figure 25.1 of “Introduction to Algorithms” by Cormen et al, 3rd ed., pg 690 */ diff --git a/Array2D/Array2D.playground/Contents.swift b/Array2D/Array2D.playground/Contents.swift index 77c0b364c..d31d77540 100644 --- a/Array2D/Array2D.playground/Contents.swift +++ b/Array2D/Array2D.playground/Contents.swift @@ -1,8 +1,3 @@ -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - /* 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. diff --git a/Array2D/Tests/Tests.xcodeproj/project.pbxproj b/Array2D/Tests/Tests.xcodeproj/project.pbxproj index a0fb1480b..af9b7c3b8 100644 --- a/Array2D/Tests/Tests.xcodeproj/project.pbxproj +++ b/Array2D/Tests/Tests.xcodeproj/project.pbxproj @@ -226,7 +226,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = swift.algorithm.club.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.2; }; name = Debug; }; @@ -238,7 +238,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = swift.algorithm.club.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.2; }; name = Release; }; diff --git a/B-Tree/BTree.playground/Contents.swift b/B-Tree/BTree.playground/Contents.swift index a0643586f..f6325513a 100644 --- a/B-Tree/BTree.playground/Contents.swift +++ b/B-Tree/BTree.playground/Contents.swift @@ -2,10 +2,7 @@ import Foundation -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif +// last checked with Xcode 10.0 let bTree = BTree(order: 1)! diff --git a/B-Tree/BTree.playground/Sources/BTree.swift b/B-Tree/BTree.playground/Sources/BTree.swift index 2693eb308..2ed0b2593 100644 --- a/B-Tree/BTree.playground/Sources/BTree.swift +++ b/B-Tree/BTree.playground/Sources/BTree.swift @@ -86,7 +86,7 @@ extension BTreeNode { } } -// MARK: BTreeNode extension: Travelsals +// MARK: BTreeNode extension: Traversals extension BTreeNode { @@ -443,7 +443,7 @@ public class BTree { } } -// MARK: BTree extension: Travelsals +// MARK: BTree extension: Traversals extension BTree { /** diff --git a/B-Tree/BTree.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/B-Tree/BTree.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/B-Tree/BTree.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/B-Tree/README.md b/B-Tree/README.md index f6924aadc..10a06853e 100644 --- a/B-Tree/README.md +++ b/B-Tree/README.md @@ -133,7 +133,7 @@ Else: ![Moving Key](Images/MovingKey.png) -####Merging two nodes +#### Merging two nodes Let's say we want to merge the child `c1` at index `i` in its parent's children array. diff --git a/B-Tree/Tests/Tests.xcodeproj/project.pbxproj b/B-Tree/Tests/Tests.xcodeproj/project.pbxproj index 3e12b9ac3..66eb85190 100644 --- a/B-Tree/Tests/Tests.xcodeproj/project.pbxproj +++ b/B-Tree/Tests/Tests.xcodeproj/project.pbxproj @@ -85,12 +85,12 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0800; + LastUpgradeCheck = 1000; ORGANIZATIONNAME = "Viktor Szilárd Simkó"; TargetAttributes = { C66702771D0EEE25008CD769 = { CreatedOnToolsVersion = 7.3.1; - LastSwiftMigration = 0800; + LastSwiftMigration = 1000; }; }; }; @@ -144,14 +144,22 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = 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_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_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -192,14 +200,22 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = 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_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_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -234,7 +250,7 @@ PRODUCT_BUNDLE_IDENTIFIER = viktorsimko.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; }; name = Debug; }; @@ -247,7 +263,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = viktorsimko.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; }; name = Release; }; diff --git a/B-Tree/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/B-Tree/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/B-Tree/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/B-Tree/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme b/B-Tree/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme index 9d6c542ae..d1555acfb 100644 --- a/B-Tree/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme +++ b/B-Tree/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme @@ -1,6 +1,6 @@ = 1 else { return } if n > 1 { - solveHanoi(n: n - 1, from: from, to: spare, spare: to) - } else { - solveHanoi(n: n - 1, from: spare, to: to, spare: from) + solveHanoi(n: n - 1, from: from, to: spare, spare: to) + solveHanoi(n: n - 1, from: spare, to: to, spare: from) } } ``` @@ -132,9 +136,9 @@ Below are some examples for each category of performance: The most trivial example of function that takes O(n!) time is given below. ```swift - func nFacFunc(n: Int) { + func nFactFunc(n: Int) { for i in stride(from: 0, to: n, by: 1) { - nFactFunc(n - 1) + nFactFunc(n: n - 1) } } ``` diff --git a/Binary Search Tree/README.markdown b/Binary Search Tree/README.markdown index 1d9321591..57d1f4bff 100644 --- a/Binary Search Tree/README.markdown +++ b/Binary Search Tree/README.markdown @@ -268,7 +268,7 @@ If there are no more nodes to look at -- when `left` or `right` is nil -- then w Searching is a recursive process, but you can also implement it with a simple loop instead: ```swift - public func search(value: T) -> BinarySearchTree? { + public func search(_ value: T) -> BinarySearchTree? { var node: BinarySearchTree? = self while let n = node { if value < n.value { @@ -375,7 +375,7 @@ As an exercise, see if you can implement filter and reduce. We can make the code more readable by defining some helper functions. ```swift - private func reconnectParentToNode(node: BinarySearchTree?) { + private func reconnectParentTo(node: BinarySearchTree?) { if let parent = parent { if isLeftChild { parent.left = node @@ -557,7 +557,7 @@ As a result, doing `tree.search(100)` gives nil. You can check whether a tree is a valid binary search tree with the following method: ```swift - public func isBST(minValue minValue: T, maxValue: T) -> Bool { + 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 @@ -615,8 +615,8 @@ This implementation is recursive, and each case of the enum will be treated diff public var height: Int { switch self { - case .Empty: return 0 - case .Leaf: return 1 + case .Empty: return -1 + case .Leaf: return 0 case let .Node(left, _, right): return 1 + max(left.height, right.height) } } diff --git a/Binary Search Tree/Solution 1/BinarySearchTree.playground/Contents.swift b/Binary Search Tree/Solution 1/BinarySearchTree.playground/Contents.swift index 8c06ae9e1..ec9902283 100644 --- a/Binary Search Tree/Solution 1/BinarySearchTree.playground/Contents.swift +++ b/Binary Search Tree/Solution 1/BinarySearchTree.playground/Contents.swift @@ -1,10 +1,5 @@ //: Playground - noun: a place where people can play -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - let tree = BinarySearchTree(value: 7) tree.insert(value: 2) tree.insert(value: 5) diff --git a/Binary Search Tree/Solution 1/BinarySearchTree.playground/Sources/BinarySearchTree.swift b/Binary Search Tree/Solution 1/BinarySearchTree.playground/Sources/BinarySearchTree.swift index 34ade3896..2d0eb7e57 100644 --- a/Binary Search Tree/Solution 1/BinarySearchTree.playground/Sources/BinarySearchTree.swift +++ b/Binary Search Tree/Solution 1/BinarySearchTree.playground/Sources/BinarySearchTree.swift @@ -235,7 +235,7 @@ extension BinarySearchTree { /* Finds the node whose value precedes our value in sorted order. */ - public func predecessor() -> BinarySearchTree? { + public func predecessor() -> BinarySearchTree? { if let left = left { return left.maximum() } else { @@ -251,7 +251,7 @@ extension BinarySearchTree { /* Finds the node whose value succeeds our value in sorted order. */ - public func successor() -> BinarySearchTree? { + public func successor() -> BinarySearchTree? { if let right = right { return right.minimum() } else { diff --git a/Binary Search Tree/Solution 1/BinarySearchTree.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Binary Search Tree/Solution 1/BinarySearchTree.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Binary Search Tree/Solution 1/BinarySearchTree.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Binary Search Tree/Solution 1/Tests/Tests.xcodeproj/project.pbxproj b/Binary Search Tree/Solution 1/Tests/Tests.xcodeproj/project.pbxproj index f4c3f65c7..d7c687e0c 100644 --- a/Binary Search Tree/Solution 1/Tests/Tests.xcodeproj/project.pbxproj +++ b/Binary Search Tree/Solution 1/Tests/Tests.xcodeproj/project.pbxproj @@ -83,12 +83,12 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0720; - LastUpgradeCheck = 0720; + LastUpgradeCheck = 1000; ORGANIZATIONNAME = "Swift Algorithm Club"; TargetAttributes = { 7B2BBC7F1C779D720067B71D = { CreatedOnToolsVersion = 7.2; - LastSwiftMigration = 0820; + LastSwiftMigration = 1000; }; }; }; @@ -141,13 +141,23 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = 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_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_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "-"; @@ -185,13 +195,23 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = 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_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_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "-"; @@ -210,6 +230,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; }; name = Release; }; @@ -221,7 +242,8 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = swift.algorithm.club.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.2; }; name = Debug; }; @@ -233,7 +255,8 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = swift.algorithm.club.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.2; }; name = Release; }; diff --git a/Binary Search Tree/Solution 1/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Binary Search Tree/Solution 1/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Binary Search Tree/Solution 1/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Binary Search Tree/Solution 1/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme b/Binary Search Tree/Solution 1/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme index 8ef8d8581..afd69e6a7 100644 --- a/Binary Search Tree/Solution 1/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme +++ b/Binary Search Tree/Solution 1/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme @@ -1,6 +1,6 @@ =4.0) -print("Hello, Swift 4!") -#endif - // Each time you insert something, you get back a completely new tree. var tree = BinarySearchTree.leaf(7) tree = tree.insert(newValue: 2) diff --git a/Binary Search Tree/Solution 2/BinarySearchTree.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Binary Search Tree/Solution 2/BinarySearchTree.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Binary Search Tree/Solution 2/BinarySearchTree.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Binary Search Tree/Solution 2/BinarySearchTree.swift b/Binary Search Tree/Solution 2/BinarySearchTree.swift index 272b86368..23b1c8aca 100644 --- a/Binary Search Tree/Solution 2/BinarySearchTree.swift +++ b/Binary Search Tree/Solution 2/BinarySearchTree.swift @@ -20,8 +20,8 @@ public enum BinarySearchTree { /* 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 .empty: return -1 + case .leaf: return 0 case let .node(left, _, right): return 1 + max(left.height, right.height) } } diff --git a/Binary Search/BinarySearch.playground/Contents.swift b/Binary Search/BinarySearch.playground/Contents.swift index 715fb5dc2..f673f25c4 100644 --- a/Binary Search/BinarySearch.playground/Contents.swift +++ b/Binary Search/BinarySearch.playground/Contents.swift @@ -1,10 +1,5 @@ //: Playground - noun: a place where people can play -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - // 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] diff --git a/Binary Search/BinarySearch.playground/Sources/BinarySearch.swift b/Binary Search/BinarySearch.playground/Sources/BinarySearch.swift deleted file mode 100644 index d6623e355..000000000 --- a/Binary Search/BinarySearch.playground/Sources/BinarySearch.swift +++ /dev/null @@ -1,52 +0,0 @@ -/** - 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/Binary Search/BinarySearch.playground/contents.xcplayground b/Binary Search/BinarySearch.playground/contents.xcplayground index 06828af92..69d154d1e 100644 --- a/Binary Search/BinarySearch.playground/contents.xcplayground +++ b/Binary Search/BinarySearch.playground/contents.xcplayground @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/Binary Search/README.markdown b/Binary Search/README.markdown index 09b141e04..0cecce6cf 100644 --- a/Binary Search/README.markdown +++ b/Binary Search/README.markdown @@ -4,15 +4,15 @@ Goal: Quickly find an element in an array. Let's say you have an array of numbers and you want to determine whether a specific number is in that array, and if so, at which index. -In most cases, Swift's `indexOf()` function is good enough for that: +In most cases, Swift's `Collection.index(of:)` function is good enough for that: ```swift let numbers = [11, 59, 3, 2, 53, 17, 31, 7, 19, 67, 47, 13, 37, 61, 29, 43, 5, 41, 23] -numbers.indexOf(43) // returns 15 +numbers.index(of: 43) // returns 15 ``` -The built-in `indexOf()` function performs a [linear search](../Linear%20Search/). In code that looks something like this: +The built-in `Collection.index(of:)` function performs a [linear search](../Linear%20Search/). In code that looks something like this: ```swift func linearSearch(_ a: [T], _ key: T) -> Int? { diff --git a/Binary Tree/BinaryTree.playground/Contents.swift b/Binary Tree/BinaryTree.playground/Contents.swift index 9da194fed..b0bff577a 100644 --- a/Binary Tree/BinaryTree.playground/Contents.swift +++ b/Binary Tree/BinaryTree.playground/Contents.swift @@ -1,10 +1,5 @@ //: Playground - noun: a place where people can play -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - public indirect enum BinaryTree { case node(BinaryTree, T, BinaryTree) case empty diff --git a/Binary Tree/BinaryTree.playground/contents.xcplayground b/Binary Tree/BinaryTree.playground/contents.xcplayground index 06828af92..69d154d1e 100644 --- a/Binary Tree/BinaryTree.playground/contents.xcplayground +++ b/Binary Tree/BinaryTree.playground/contents.xcplayground @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/Binary Tree/BinaryTree.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Binary Tree/BinaryTree.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Binary Tree/BinaryTree.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Binary Tree/BinaryTree.swift b/Binary Tree/BinaryTree.swift index 102f23e06..6146880b4 100644 --- a/Binary Tree/BinaryTree.swift +++ b/Binary Tree/BinaryTree.swift @@ -29,7 +29,7 @@ extension BinaryTree: CustomStringConvertible { } extension BinaryTree { - public func traverseInOrder(process: T -> Void) { + public func traverseInOrder(process: (T) -> Void) { if case let .node(left, value, right) = self { left.traverseInOrder(process: process) process(value) @@ -37,7 +37,7 @@ extension BinaryTree { } } - public func traversePreOrder(process: T -> Void) { + public func traversePreOrder(process: (T) -> Void) { if case let .node(left, value, right) = self { process(value) left.traversePreOrder(process: process) @@ -45,7 +45,7 @@ extension BinaryTree { } } - public func traversePostOrder(process: T -> Void) { + public func traversePostOrder(process: (T) -> Void) { if case let .node(left, value, right) = self { left.traversePostOrder(process: process) right.traversePostOrder(process: process) diff --git a/Bit Set/BitSet.playground/Contents.swift b/Bit Set/BitSet.playground/Contents.swift index 95253ea27..70d5ca5e8 100644 --- a/Bit Set/BitSet.playground/Contents.swift +++ b/Bit Set/BitSet.playground/Contents.swift @@ -1,10 +1,5 @@ //: Playground - noun: a place where people can play -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - // Create a bit set that stores 140 bits var bits = BitSet(size: 140) @@ -94,3 +89,23 @@ 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/Bit Set/BitSet.playground/Sources/BitSet.swift b/Bit Set/BitSet.playground/Sources/BitSet.swift index 59306f5a5..258bce457 100644 --- a/Bit Set/BitSet.playground/Sources/BitSet.swift +++ b/Bit Set/BitSet.playground/Sources/BitSet.swift @@ -9,7 +9,7 @@ public struct BitSet { We store the bits in a list of unsigned 64-bit integers. The first entry, `words[0]`, is the least significant word. */ - private let N = 64 + fileprivate let N = 64 public typealias Word = UInt64 fileprivate(set) public var words: [Word] @@ -221,6 +221,50 @@ prefix public func ~ (rhs: BitSet) -> BitSet { return out } +// MARK: - Bit shift operations + +/* + Note: For bitshift operations, the assumption is that any bits that are + shifted off the end of the end of the declared size are not still set. + In other words, we are maintaining the original number of bits. + */ + +public func << (lhs: BitSet, numBitsLeft: Int) -> 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 { diff --git a/Bit Set/BitSet.playground/contents.xcplayground b/Bit Set/BitSet.playground/contents.xcplayground index 06828af92..69d154d1e 100644 --- a/Bit Set/BitSet.playground/contents.xcplayground +++ b/Bit Set/BitSet.playground/contents.xcplayground @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/Bit Set/BitSet.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Bit Set/BitSet.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Bit Set/BitSet.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Bit Set/BitSet.swift b/Bit Set/BitSet.swift deleted file mode 100644 index cfe8517b5..000000000 --- a/Bit Set/BitSet.swift +++ /dev/null @@ -1,248 +0,0 @@ -/* - 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. - */ - private 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 { - for i in 0.. Bool { - for x in words { - if x != 0 { return true } - } - return false - } - - /* Checks if none of the bits are set. */ - public func all0() -> Bool { - for x in words { - if x != 0 { 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 { - /* Based on the hashing code from Java's BitSet. */ - public var hashValue: Int { - var h = Word(1234) - for i in stride(from: words.count, to: 0, by: -1) { - h ^= words[i - 1] &* Word(i) - } - return Int((h >> 32) ^ h) - } -} - -// 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.. 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/Bit Set/README.markdown b/Bit Set/README.markdown index 6bb6e7c89..365950d06 100644 --- a/Bit Set/README.markdown +++ b/Bit Set/README.markdown @@ -329,6 +329,90 @@ This is the original value but with the lowest 1-bit removed. We keep repeating this process until the value consists of all zeros. The time complexity is **O(s)** where **s** is the number of 1-bits. +## Bit Shift Operations + +Bit shifts are a common and very useful mechanism when dealing with bitsets. Here is the right-shift function: + +``` +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 +} +``` + +Let's start with this line: + +```swift +for i in 0..> 10 + +I've grouped each part of the number by word to make it easier to see what happens. The for-loop goes from least significant word to most significant. So for index zero we're want to know what bits will make up our least significant word. Let's calculate our offset and shift values: + + offset = 10 / 8 = 1 (remember this is integer division) + shift = 10 % 8 = 2 + +So we consult the word at offset 1 to get some of our bits: + + 11000000 >> 2 = 00110000 + +And we get the rest of them from the word one further away: + + 01000010 << (8 - 2) = 10000000 + +And we bitwise OR these together to get our least significant term + + 00110000 + 10000000 + -------- OR + 10110000 + +We repeat this for the 2nd least significant term and obtain: + + 00010000 + +The last term can't get any bits because they are past the end of our number so those are all zeros. Our result is: + + 00000000 00010000 10110000 + ## See also [Bit Twiddling Hacks](http://graphics.stanford.edu/~seander/bithacks.html) diff --git a/Bloom Filter/BloomFilter.playground/Contents.swift b/Bloom Filter/BloomFilter.playground/Contents.swift index 9e4a89351..7a3719d57 100644 --- a/Bloom Filter/BloomFilter.playground/Contents.swift +++ b/Bloom Filter/BloomFilter.playground/Contents.swift @@ -1,8 +1,5 @@ //: Playground - noun: a place where people can play -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif + public class BloomFilter { fileprivate var array: [Bool] private var hashFunctions: [(T) -> Int] @@ -54,7 +51,7 @@ public class BloomFilter { func djb2(x: String) -> Int { var hash = 5381 - for char in x.characters { + for char in x { hash = ((hash << 5) &+ hash) &+ char.hashValue } return Int(hash) @@ -62,7 +59,7 @@ func djb2(x: String) -> Int { func sdbm(x: String) -> Int { var hash = 0 - for char in x.characters { + for char in x { hash = char.hashValue &+ (hash << 6) &+ (hash << 16) &- hash } return Int(hash) diff --git a/Bloom Filter/BloomFilter.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Bloom Filter/BloomFilter.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Bloom Filter/BloomFilter.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Bloom Filter/BloomFilter.playground/timeline.xctimeline b/Bloom Filter/BloomFilter.playground/timeline.xctimeline deleted file mode 100644 index bf468afec..000000000 --- a/Bloom Filter/BloomFilter.playground/timeline.xctimeline +++ /dev/null @@ -1,6 +0,0 @@ - - - - - diff --git a/Bloom Filter/README.markdown b/Bloom Filter/README.markdown index 3fff0a379..5f2ede069 100644 --- a/Bloom Filter/README.markdown +++ b/Bloom Filter/README.markdown @@ -100,4 +100,31 @@ public func query(_ value: T) -> Bool { If you're coming from another imperative language, you might notice the unusual syntax in the `exists` assignment. Swift makes use of functional paradigms when it makes code more consise and readable, and in this case `reduce` is a much more consise way to check if all the required bits are `true` than a `for` loop. -*Written for Swift Algorithm Club by Jamil Dhanani. Edited by Matthijs Hollemans.* +## Another approach + +In the previous section, you learnt about how using multiple different hash functions can help reduce the chance of collisions in the bloom filter. However, good hash functions are difficult to design. A simple alternative to multiple hash functions is to use a set of random numbers. + +As an example, let's say a bloom filter wants to hash each element 15 times during insertion. Instead of using 15 different hash functions, you can rely on just one hash function. The hash value can then be combined with 15 different values to form the indices for flipping. This bloom filter would initialize a set of 15 random numbers ahead of time and use these values during each insertion. + +``` +hash("Hello world!") >> hash(987654321) // would flip bit 8 +hash("Hello world!") >> hash(123456789) // would flip bit 2 +``` + +Since Swift 4.2, `Hasher` is now included in the Standard library, which is designed to reduce multiple hashes to a single hash in an efficient manner. This makes combining the hashes trivial. + +``` +private func computeHashes(_ value: T) -> [Int] { + return randomSeeds.map() { seed in + let hasher = Hasher() + hasher.combine(seed) + hasher.combine(value) + let hashValue = hasher.finalize() + return abs(hashValue % array.count) + } +} +``` + +If you want to learn more about this approach, you can read about the [Hasher documentation](https://developer.apple.com/documentation/swift/hasher) or Soroush Khanlou's [Swift 4.2 Bloom filter](http://khanlou.com/2018/09/bloom-filters/) implementation. + +*Written for Swift Algorithm Club by Jamil Dhanani. Edited by Matthijs Hollemans. Updated by Bruno Scheele.* diff --git a/Bloom Filter/Tests/BloomFilterTests.swift b/Bloom Filter/Tests/BloomFilterTests.swift index 30df702a7..d88ec0a31 100644 --- a/Bloom Filter/Tests/BloomFilterTests.swift +++ b/Bloom Filter/Tests/BloomFilterTests.swift @@ -6,7 +6,7 @@ import XCTest func djb2(_ x: String) -> Int { var hash = 5381 - for char in x.characters { + for char in x { hash = ((hash << 5) &+ hash) &+ char.hashValue } @@ -16,7 +16,7 @@ func djb2(_ x: String) -> Int { func sdbm(_ x: String) -> Int { var hash = 0 - for char in x.characters { + for char in x { hash = char.hashValue &+ (hash << 6) &+ (hash << 16) &- hash } diff --git a/Bloom Filter/Tests/Tests.xcodeproj/project.pbxproj b/Bloom Filter/Tests/Tests.xcodeproj/project.pbxproj index 6493fd153..f0649ba26 100644 --- a/Bloom Filter/Tests/Tests.xcodeproj/project.pbxproj +++ b/Bloom Filter/Tests/Tests.xcodeproj/project.pbxproj @@ -83,12 +83,12 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0720; - LastUpgradeCheck = 0800; + LastUpgradeCheck = 1000; ORGANIZATIONNAME = "Swift Algorithm Club"; TargetAttributes = { 7B2BBC7F1C779D720067B71D = { CreatedOnToolsVersion = 7.2; - LastSwiftMigration = 0800; + LastSwiftMigration = 1000; }; }; }; @@ -141,14 +141,22 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = 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_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_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -188,14 +196,22 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = 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_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_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -228,7 +244,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = swift.algorithm.club.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; }; name = Debug; }; @@ -240,7 +256,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = swift.algorithm.club.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; }; name = Release; }; diff --git a/Bloom Filter/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Bloom Filter/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Bloom Filter/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Bloom Filter/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme b/Bloom Filter/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme index 14f27f777..afd69e6a7 100644 --- a/Bloom Filter/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme +++ b/Bloom Filter/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme @@ -1,6 +1,6 @@ =4.0) -print("Hello, Swift 4!") -#endif - struct Message: Comparable, CustomStringConvertible { let name: String let priority: Int diff --git a/Bounded Priority Queue/BoundedPriorityQueue.playground/contents.xcplayground b/Bounded Priority Queue/BoundedPriorityQueue.playground/contents.xcplayground index 06828af92..69d154d1e 100644 --- a/Bounded Priority Queue/BoundedPriorityQueue.playground/contents.xcplayground +++ b/Bounded Priority Queue/BoundedPriorityQueue.playground/contents.xcplayground @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/Bounded Priority Queue/BoundedPriorityQueue.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Bounded Priority Queue/BoundedPriorityQueue.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Bounded Priority Queue/BoundedPriorityQueue.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Bounded Priority Queue/Tests/Tests.xcodeproj/project.pbxproj b/Bounded Priority Queue/Tests/Tests.xcodeproj/project.pbxproj index 842c51695..11f907b47 100644 --- a/Bounded Priority Queue/Tests/Tests.xcodeproj/project.pbxproj +++ b/Bounded Priority Queue/Tests/Tests.xcodeproj/project.pbxproj @@ -86,7 +86,7 @@ TargetAttributes = { B80004B21C83E342001FE2D7 = { CreatedOnToolsVersion = 7.2.1; - LastSwiftMigration = 0820; + LastSwiftMigration = 1000; }; }; }; @@ -226,7 +226,7 @@ SDKROOT = macosx; SWIFT_OBJC_BRIDGING_HEADER = ""; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.2; }; name = Debug; }; @@ -269,7 +269,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SDKROOT = macosx; SWIFT_OBJC_BRIDGING_HEADER = ""; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.2; }; name = Release; }; diff --git a/Bounded Priority Queue/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Bounded Priority Queue/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Bounded Priority Queue/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Boyer-Moore-Horspool/BoyerMooreHorspool.playground/Contents.swift b/Boyer-Moore-Horspool/BoyerMooreHorspool.playground/Contents.swift index e88a1b93d..933cbc4ac 100644 --- a/Boyer-Moore-Horspool/BoyerMooreHorspool.playground/Contents.swift +++ b/Boyer-Moore-Horspool/BoyerMooreHorspool.playground/Contents.swift @@ -1,10 +1,5 @@ //: Playground - noun: a place where people can play -// last checked with Xcode 9.0b4 -#if swift(>=4.0) - print("Hello, Swift 4!") -#endif - /* Boyer-Moore string search @@ -16,13 +11,13 @@ 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.characters.count - guard patternLength > 0, patternLength <= characters.count else { return nil } + 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.characters.enumerated() { + for (i, c) in pattern.enumerated() { skipTable[c] = patternLength - i - 1 } diff --git a/Boyer-Moore-Horspool/BoyerMooreHorspool.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Boyer-Moore-Horspool/BoyerMooreHorspool.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Boyer-Moore-Horspool/BoyerMooreHorspool.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Boyer-Moore-Horspool/BoyerMooreHorspool.playground/timeline.xctimeline b/Boyer-Moore-Horspool/BoyerMooreHorspool.playground/timeline.xctimeline index 89bc76f90..2688d72c1 100644 --- a/Boyer-Moore-Horspool/BoyerMooreHorspool.playground/timeline.xctimeline +++ b/Boyer-Moore-Horspool/BoyerMooreHorspool.playground/timeline.xctimeline @@ -3,17 +3,17 @@ version = "3.0"> diff --git a/Boyer-Moore-Horspool/BoyerMooreHorspool.swift b/Boyer-Moore-Horspool/BoyerMooreHorspool.swift index af8aea9d9..481b7d483 100644 --- a/Boyer-Moore-Horspool/BoyerMooreHorspool.swift +++ b/Boyer-Moore-Horspool/BoyerMooreHorspool.swift @@ -9,13 +9,13 @@ 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.characters.count - guard patternLength > 0, patternLength <= characters.count else { return nil } - + 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.characters.enumerated() { + for (i, c) in pattern.enumerated() { skipTable[c] = patternLength - i - 1 } diff --git a/Boyer-Moore-Horspool/README.markdown b/Boyer-Moore-Horspool/README.markdown index f5e21ea41..8879d8df6 100644 --- a/Boyer-Moore-Horspool/README.markdown +++ b/Boyer-Moore-Horspool/README.markdown @@ -38,13 +38,13 @@ extension String { func index(of pattern: String) -> 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.characters.count - guard patternLength > 0, patternLength <= characters.count else { return nil } + let patternLength = pattern.count + guard patternLength > 0, patternLength <= 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.characters.enumerated() { + for (i, c) in pattern.enumerated() { skipTable[c] = patternLength - i - 1 } @@ -162,13 +162,13 @@ extension String { func index(of pattern: String) -> 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.characters.count + let patternLength = pattern.count guard patternLength > 0, patternLength <= characters.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.characters.enumerated() { + for (i, c) in pattern.enumerated() { skipTable[c] = patternLength - i - 1 } diff --git a/Boyer-Moore-Horspool/Tests/BoyerMooreTests.swift b/Boyer-Moore-Horspool/Tests/BoyerMooreTests.swift index 9efe60874..436173127 100755 --- a/Boyer-Moore-Horspool/Tests/BoyerMooreTests.swift +++ b/Boyer-Moore-Horspool/Tests/BoyerMooreTests.swift @@ -26,8 +26,8 @@ class BoyerMooreTest: XCTestCase { XCTAssertNotNil(index) let startIndex = index! - let endIndex = string.index(index!, offsetBy: pattern.characters.count) - let match = string.substring(with: startIndex.. + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Boyer-Moore-Horspool/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme b/Boyer-Moore-Horspool/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme index 538a6f4fa..afd69e6a7 100644 --- a/Boyer-Moore-Horspool/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme +++ b/Boyer-Moore-Horspool/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme @@ -1,6 +1,6 @@ =4.0) -print("Hello, Swift 4!") -#endif func breadthFirstSearch(_ graph: Graph, source: Node) -> [String] { var queue = Queue() diff --git a/Breadth-First Search/BreadthFirstSearch.playground/Sources/Graph.swift b/Breadth-First Search/BreadthFirstSearch.playground/Sources/Graph.swift index 87e21897c..0343120f8 100644 --- a/Breadth-First Search/BreadthFirstSearch.playground/Sources/Graph.swift +++ b/Breadth-First Search/BreadthFirstSearch.playground/Sources/Graph.swift @@ -5,7 +5,7 @@ public class Graph: CustomStringConvertible, Equatable { self.nodes = [] } - public func addNode(_ label: String) -> Node { + @discardableResult public func addNode(_ label: String) -> Node { let node = Node(label) nodes.append(node) return node diff --git a/Breadth-First Search/BreadthFirstSearch.playground/contents.xcplayground b/Breadth-First Search/BreadthFirstSearch.playground/contents.xcplayground index 513c2e7e9..f635e9804 100644 --- a/Breadth-First Search/BreadthFirstSearch.playground/contents.xcplayground +++ b/Breadth-First Search/BreadthFirstSearch.playground/contents.xcplayground @@ -1,2 +1,2 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/Breadth-First Search/BreadthFirstSearch.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Breadth-First Search/BreadthFirstSearch.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Breadth-First Search/BreadthFirstSearch.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Breadth-First Search/README.markdown b/Breadth-First Search/README.markdown index b2298822f..097f4278d 100644 --- a/Breadth-First Search/README.markdown +++ b/Breadth-First Search/README.markdown @@ -151,7 +151,7 @@ This will output: `["a", "b", "c", "d", "e", "f", "g", "h"]` Breadth-first search can be used to solve many problems. A small selection: -* Computing the [shortest path](../Shortest%20Path/) between a source node and each of the other nodes (only for unweighted graphs). +* Computing the [shortest path](../Shortest%20Path%20(Unweighted)/) between a source node and each of the other nodes (only for unweighted graphs). * Calculating the [minimum spanning tree](../Minimum%20Spanning%20Tree%20(Unweighted)/) on an unweighted graph. *Written by [Chris Pilcher](https://github.com/chris-pilcher) and Matthijs Hollemans* diff --git a/Breadth-First Search/Tests/Tests.xcodeproj/project.pbxproj b/Breadth-First Search/Tests/Tests.xcodeproj/project.pbxproj index f86abf86e..a7f04e00a 100644 --- a/Breadth-First Search/Tests/Tests.xcodeproj/project.pbxproj +++ b/Breadth-First Search/Tests/Tests.xcodeproj/project.pbxproj @@ -59,7 +59,6 @@ 7B2BBC941C779E7B0067B71D /* Info.plist */, ); name = Tests; - path = TestsTests; sourceTree = ""; }; /* End PBXGroup section */ @@ -89,12 +88,12 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0720; - LastUpgradeCheck = 0820; + LastUpgradeCheck = 1000; ORGANIZATIONNAME = "Swift Algorithm Club"; TargetAttributes = { 7B2BBC7F1C779D720067B71D = { CreatedOnToolsVersion = 7.2; - LastSwiftMigration = 0820; + LastSwiftMigration = 1000; }; }; }; @@ -149,13 +148,23 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = 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_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_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "-"; @@ -193,13 +202,23 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = 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_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_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "-"; @@ -218,6 +237,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; }; name = Release; }; @@ -231,7 +251,7 @@ PRODUCT_BUNDLE_IDENTIFIER = swift.algorithm.club.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.2; }; name = Debug; }; @@ -244,7 +264,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = swift.algorithm.club.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.2; }; name = Release; }; diff --git a/Breadth-First Search/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Breadth-First Search/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Breadth-First Search/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Breadth-First Search/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme b/Breadth-First Search/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme index dfcf6de42..afd69e6a7 100644 --- a/Breadth-First Search/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme +++ b/Breadth-First Search/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme @@ -1,6 +1,6 @@ )) diff --git a/Bubble Sort/MyPlayground.playground/Sources/BubbleSort.swift b/Bubble Sort/MyPlayground.playground/Sources/BubbleSort.swift new file mode 100644 index 000000000..09e01542b --- /dev/null +++ b/Bubble Sort/MyPlayground.playground/Sources/BubbleSort.swift @@ -0,0 +1,46 @@ +// +// BubbleSort.swift +// +// Created by Julio Brazil on 1/10/18. +// +// 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. +// + +import Foundation + +/// Performs the bubble sort algorithm in the array +/// +/// - Parameter elements: a array of elements that implement the Comparable protocol +/// - Returns: an array with the same elements but in order +public func bubbleSort (_ elements: [T]) -> [T] where T: Comparable { + return bubbleSort(elements, <) +} + +public func bubbleSort (_ elements: [T], _ comparison: (T,T) -> Bool) -> [T] { + var array = elements + + for i in 0.. + + + \ No newline at end of file diff --git a/Ordered Set/OrderedSet.playground/playground.xcworkspace/contents.xcworkspacedata b/Bubble Sort/MyPlayground.playground/playground.xcworkspace/contents.xcworkspacedata similarity index 100% rename from Ordered Set/OrderedSet.playground/playground.xcworkspace/contents.xcworkspacedata rename to Bubble Sort/MyPlayground.playground/playground.xcworkspace/contents.xcworkspacedata diff --git a/Bubble Sort/MyPlayground.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Bubble Sort/MyPlayground.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Bubble Sort/MyPlayground.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Bubble Sort/README.markdown b/Bubble Sort/README.markdown index 93d370827..211a5aefa 100644 --- a/Bubble Sort/README.markdown +++ b/Bubble Sort/README.markdown @@ -1,6 +1,6 @@ # Bubble Sort -Bubble sort is a sorting algorithm that is implemented by starting in the beginning of the array and swapping the first two elements only if the first element is greater than the second element. This comparison is then moved onto the next pair and so on and so forth. This is done until the array is completely sorted. The smaller items slowly “bubble” up to the beginning of the array. +Bubble sort is a sorting algorithm that is implemented by starting in the beginning of the array and swapping the first two elements only if the first element is greater than the second element. This comparison is then moved onto the next pair and so on and so forth. This is done until the array is completely sorted. The smaller items slowly “bubble” up to the beginning of the array. Sometimes this algorithm is refered as Sinking sort, due to the larger, or heavier elements sinking to the end of the array. ##### Runtime: - Average: O(N^2) @@ -12,3 +12,116 @@ Bubble sort is a sorting algorithm that is implemented by starting in the beginn ### Implementation: The implementation will not be shown as the average and worst runtimes show that this is a very inefficient algorithm. However, having a grasp of the concept will help you understand the basics of simple sorting algorithms. + +Bubble sort is a very simple sorting algorithm, it consists in comparing pairs of adjacent elements in the array, if the first element is larger, swap them, otherwise, you do nothing and go for the next comparison. +This is accomplished by looking through the array `n` times, `n` being the amount of elements in the array. + +![animated gif of the bubble sort algorithm](https://s3.amazonaws.com/codecademy-content/programs/tdd-js/articles/BubbleSort.gif) + +This GIF shows a inverted implementation than + +#### Example +Let us take the array `[5, 1, 4, 2, 8]`, and sort the array from lowest number to greatest number using bubble sort. In each step, elements written in bold are being compared. Three passes will be required. + +##### First Pass +[ **5 1** 4 2 8 ] -> [ **1 5** 4 2 8 ], Here, algorithm compares the first two elements, and swaps since 5 > 1. + +[ 1 **5 4** 2 8 ] -> [ 1 **4 5** 2 8 ], Swap since 5 > 4 + +[ 1 4 **5 2** 8 ] -> [ 1 4 **2 5** 8 ], Swap since 5 > 2 + +[ 1 4 2 **5 8** ] -> [ 1 4 2 **5 8** ], Now, since these elements are already in order (8 > 5), algorithm does not swap them. + +##### Second Pass +[ **1 4** 2 5 8 ] -> [ **1 4** 2 5 8 ] + +[ 1 **4 2** 5 8 ] -> [ 1 **2 4** 5 8 ], Swap since 4 > 2 + +[ 1 2 **4 5** 8 ] -> [ 1 2 **4 5** 8 ] + +[ 1 2 4 **5 8** ] -> [ 1 2 4 **5 8** ] +Now, the array is already sorted, but the algorithm does not know if it is completed. The algorithm needs one whole pass without any swap to know it is sorted. + +##### Third Pass +[ **1 2** 4 5 8 ] -> [ **1 2** 4 5 8 ] + +[ 1 **2 4** 5 8 ] -> [ 1 **2 4** 5 8 ] + +[ 1 2 **4 5** 8 ] -> [ 1 2 **4 5** 8 ] + +[ 1 2 4 **5 8** ] -> [ 1 2 4 **5 8** ] + +This is the same for the forth and fifth passes. + +#### Code +```swift +for i in 0.. [ **1 5** 4 2 8 ], Swaps since 5 > 1 + +[ 1 **5 4** 2 8 ] -> [ 1 **4 5** 2 8 ], Swap since 5 > 4 + +[ 1 4 **5 2** 8 ] -> [ 1 4 **2 5** 8 ], Swap since 5 > 2 + +[ 1 4 2 **5 8** ] -> [ 1 4 2 **5 8** ], Now, since these elements are already in order (8 > 5), algorithm does not swap them. + +*by the end of the first pass, the last element is guaranteed to be the largest* + +##### Second Pass +[ **1 4** 2 5 8 ] -> [ **1 4** 2 5 8 ] + +[ 1 **4 2** 5 8 ] -> [ 1 **2 4** 5 8 ], Swap since 4 > 2 + +[ 1 2 **4 5** 8 ] -> [ 1 2 **4 5** 8 ], As the first loop has occured once, the inner loop stops here, not comparing 5 with 8 + +##### Third Pass +[ **1 2** 4 5 8 ] -> [ **1 2** 4 5 8 ] + +[ 1 **2 4** 5 8 ] -> [ 1 **2 4** 5 8 ] again, stoping one comparison short + +##### Fourth Pass +[ **1 2** 4 5 8 ] -> [ **1 2** 4 5 8 ] + +There is no Fifth pass + +#### Conclusion + +Even with the proposed optimizations, this is still a terribly inefficient sorting algorithm. A good alternative is [Merge Sort](https://github.com/raywenderlich/swift-algorithm-club/tree/master/Merge%20Sort), that not only is better performing, has a similar degree of dificulty to implement. + +*Updated for the Swift Algorithm Club by Julio Brazil* + +##### Supporting Links +[Code Pumpkin](https://codepumpkin.com/bubble-sort/) +[Wikipedia](https://en.wikipedia.org/wiki/Bubble_sort) +[GeeksforGeeks](https://www.geeksforgeeks.org/bubble-sort/) diff --git a/Bucket Sort/BucketSort.playground/Sources/BucketSort.swift b/Bucket Sort/BucketSort.playground/Sources/BucketSort.swift index bafd216fa..64408eca4 100644 --- a/Bucket Sort/BucketSort.playground/Sources/BucketSort.swift +++ b/Bucket Sort/BucketSort.playground/Sources/BucketSort.swift @@ -20,31 +20,6 @@ // // -import Foundation -// FIXME: comparison operators with optionals were removed from the Swift Standard Libary. -// Consider refactoring the code to use the non-optional operators. -fileprivate func < (lhs: T?, rhs: T?) -> Bool { - switch (lhs, rhs) { - case let (l?, r?): - return l < r - case (nil, _?): - return true - default: - return false - } -} - -// FIXME: comparison operators with optionals were removed from the Swift Standard Libary. -// Consider refactoring the code to use the non-optional operators. -fileprivate func >= (lhs: T?, rhs: T?) -> Bool { - switch (lhs, rhs) { - case let (l?, r?): - return l >= r - default: - return !(lhs < rhs) - } -} - ////////////////////////////////////// // MARK: Main algorithm ////////////////////////////////////// @@ -87,7 +62,11 @@ private func enoughSpaceInBuckets(_ buckets: [Bucket], elements: [T]) -> B let maximumValue = elements.max()?.toInt() let totalCapacity = buckets.count * (buckets.first?.capacity)! - return totalCapacity >= maximumValue + guard let max = maximumValue else { + return false + } + + return totalCapacity >= max } ////////////////////////////////////// diff --git a/Bucket Sort/BucketSort.playground/contents.xcplayground b/Bucket Sort/BucketSort.playground/contents.xcplayground index 5da2641c9..9f5f2f40c 100644 --- a/Bucket Sort/BucketSort.playground/contents.xcplayground +++ b/Bucket Sort/BucketSort.playground/contents.xcplayground @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/Bucket Sort/BucketSort.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Bucket Sort/BucketSort.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Bucket Sort/BucketSort.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Bucket Sort/BucketSort.swift b/Bucket Sort/BucketSort.swift index 108073288..4c2abd372 100644 --- a/Bucket Sort/BucketSort.swift +++ b/Bucket Sort/BucketSort.swift @@ -20,31 +20,6 @@ // // -import Foundation -// FIXME: comparison operators with optionals were removed from the Swift Standard Libary. -// Consider refactoring the code to use the non-optional operators. -fileprivate func < (lhs: T?, rhs: T?) -> Bool { - switch (lhs, rhs) { - case let (l?, r?): - return l < r - case (nil, _?): - return true - default: - return false - } -} - -// FIXME: comparison operators with optionals were removed from the Swift Standard Libary. -// Consider refactoring the code to use the non-optional operators. -fileprivate func >= (lhs: T?, rhs: T?) -> Bool { - switch (lhs, rhs) { - case let (l?, r?): - return l >= r - default: - return !(lhs < rhs) - } -} - ////////////////////////////////////// // MARK: Main algorithm ////////////////////////////////////// @@ -87,7 +62,11 @@ private func enoughSpaceInBuckets(_ buckets: [Bucket], elements: [T]) -> B let maximumValue = elements.max()?.toInt() let totalCapacity = buckets.count * (buckets.first?.capacity)! - return totalCapacity >= maximumValue + guard let max = maximumValue else { + return false + } + + return totalCapacity >= max } ////////////////////////////////////// diff --git a/Bucket Sort/README.markdown b/Bucket Sort/README.markdown index f572fc9a1..f2708d71e 100644 --- a/Bucket Sort/README.markdown +++ b/Bucket Sort/README.markdown @@ -256,4 +256,4 @@ The following are some of the variation to the general [Bucket Sort](https://en. - [Postman Sort](https://en.wikipedia.org/wiki/Bucket_sort#Postman.27s_sort) - [Shuffle Sort](https://en.wikipedia.org/wiki/Bucket_sort#Shuffle_sort) -*Written for Swift Algorithm Club by Barbara Rodeker. Images from Wikipedia.* +*Written for Swift Algorithm Club by Barbara Rodeker. Images from Wikipedia. Updated by Bruno Scheele.* diff --git a/Bucket Sort/Tests/Tests.xcodeproj/project.pbxproj b/Bucket Sort/Tests/Tests.xcodeproj/project.pbxproj index 84a305784..6df507720 100644 --- a/Bucket Sort/Tests/Tests.xcodeproj/project.pbxproj +++ b/Bucket Sort/Tests/Tests.xcodeproj/project.pbxproj @@ -83,7 +83,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0900; + LastUpgradeCheck = 1000; ORGANIZATIONNAME = "Swift Algorithm Club"; TargetAttributes = { 7B2BBC7F1C779D720067B71D = { @@ -145,12 +145,14 @@ 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_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; @@ -197,12 +199,14 @@ 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_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; diff --git a/Bucket Sort/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Bucket Sort/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Bucket Sort/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Bucket Sort/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme b/Bucket Sort/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme index 538a6f4fa..afd69e6a7 100644 --- a/Bucket Sort/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme +++ b/Bucket Sort/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme @@ -1,6 +1,6 @@ (minimum:Double, firstPoint:Point, secondPoint:Point) { + var innerPoints = mergeSort(points, sortAccording : true) + let result = ClosestPair(&innerPoints, innerPoints.count) + return (result.minValue, result.firstPoint, result.secondPoint) +} + +func ClosestPair(_ p : inout [Point],_ n : Int) -> (minValue: Double,firstPoint: Point,secondPoint: Point) +{ + // Brute force if only 3 points (To end recursion) + if n <= 3 + { + var i=0, j = i+1 + var minDist = Double.infinity + var newFirst:Point? = nil + var newSecond:Point? = nil + while i min { break } + if dist(strip[i], strip[x]) < temp + { + temp = dist(strip[i], strip[x]) + tempFirst = strip[i] + tempSecond = strip[x] + } + x+=1 + } + i+=1 + } + + if temp < min + { + min = temp; + first = tempFirst + second = tempSecond + } + return (min, first, second) +} + + + + +// MergeSort the array (Taken from Swift Algorithms Club with +// minor addition) +// sortAccodrding : true -> x, false -> y. +func mergeSort(_ array: [Point], sortAccording : Bool) -> [Point] { + guard array.count > 1 else { return array } + let middleIndex = array.count / 2 + let leftArray = mergeSort(Array(array[0.. [Point] { + + var compare : (Point, Point) -> Bool + + // Choose to compare with X or Y. + if sortAccording + { + compare = { p1,p2 in + return p1.x < p2.x + } + } + else + { + compare = { p1, p2 in + return p1.y < p2.y + } + } + + var leftIndex = 0 + var rightIndex = 0 + var orderedPile = [Point]() + if orderedPile.capacity < leftPile.count + rightPile.count { + orderedPile.reserveCapacity(leftPile.count + rightPile.count) + } + + while true { + guard leftIndex < leftPile.endIndex else { + orderedPile.append(contentsOf: rightPile[rightIndex.. Double +{ + let equation:Double = (((a.x-b.x)*(a.x-b.x))) + (((a.y-b.y)*(a.y-b.y))) + return equation.squareRoot() +} + + +var a = Point(0,2) +var b = Point(6,67) +var c = Point(43,71) +var d = Point(1000,1000) +var e = Point(39,107) +var f = Point(2000,2000) +var g = Point(3000,3000) +var h = Point(4000,4000) + + +var points = [a,b,c,d,e,f,g,h] +let endResult = ClosestPairOf(points: points) +print("Minimum Distance : \(endResult.minimum), The two points : (\(endResult.firstPoint.x ),\(endResult.firstPoint.y)), (\(endResult.secondPoint.x),\(endResult.secondPoint.y))") + diff --git a/Closest Pair/ClosestPair.playground/contents.xcplayground b/Closest Pair/ClosestPair.playground/contents.xcplayground new file mode 100644 index 000000000..a93d4844a --- /dev/null +++ b/Closest Pair/ClosestPair.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Closest Pair/ClosestPair.playground/playground.xcworkspace/contents.xcworkspacedata b/Closest Pair/ClosestPair.playground/playground.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/Closest Pair/ClosestPair.playground/playground.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Closest Pair/ClosestPair.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Closest Pair/ClosestPair.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Closest Pair/ClosestPair.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Closest Pair/ClosestPair.playground/playground.xcworkspace/xcuserdata/admin.xcuserdatad/UserInterfaceState.xcuserstate b/Closest Pair/ClosestPair.playground/playground.xcworkspace/xcuserdata/admin.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 000000000..82b130340 Binary files /dev/null and b/Closest Pair/ClosestPair.playground/playground.xcworkspace/xcuserdata/admin.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Closest Pair/Images/1200px-Closest_pair_of_points.png b/Closest Pair/Images/1200px-Closest_pair_of_points.png new file mode 100644 index 000000000..cf61a3eff Binary files /dev/null and b/Closest Pair/Images/1200px-Closest_pair_of_points.png differ diff --git a/Closest Pair/Images/Case.png b/Closest Pair/Images/Case.png new file mode 100644 index 000000000..20b171a1a Binary files /dev/null and b/Closest Pair/Images/Case.png differ diff --git a/Closest Pair/Images/Strip.png b/Closest Pair/Images/Strip.png new file mode 100644 index 000000000..b0193a127 Binary files /dev/null and b/Closest Pair/Images/Strip.png differ diff --git a/Closest Pair/README.markdown b/Closest Pair/README.markdown new file mode 100644 index 000000000..a4de4a28b --- /dev/null +++ b/Closest Pair/README.markdown @@ -0,0 +1,88 @@ +# ClosestPair + +Closest Pair is an algorithm that finds the closest pair of a given array of points By utilizing the Divide and Conquer methodology of solving problems so that it reaches the correct solution with O(nlogn) complexity. + +![Given points and we're required to find the two red ones](Images/1200px-Closest_pair_of_points.png) + +As we see in the above image there are an array of points and we need to find the closest two, But how do we do that without having to compare each two points which results in a whopping O(n^2) complexity? + +Here is the main algorithm (Steps) we'll follow. + +- Sort the array according to their position on the X-axis so that they are sorted in the array as they are naturally in math. + +```swift +var innerPoints = mergeSort(points, sortAccording : true) +``` + +- Divide the points into two arrays Left, Right and keep dividing until you reach to only having 3 points in your array. + +- The base case is you have less than 3 points compare those against each other (Brute force) then return the minimum distance you found and the two points. + +- Now we get the first observation in the below image, There could be 2 points both very close to each other and indeed those two are the closest pair but since our algorithm so far divides from the middle + +```swift +let line:Double = (p[mid].x + p[mid+1].x)/2 +``` + +and just recursively calls itself until it reaches the base case we don't detect those points. + +![ Points lying near the division line](Images/Case.png) + +- To solve this we start by sorting the array on the Y-axis to get the points in their natural order and then we start getting the difference between the X position of the point and the line we drew to divide and if it is less than the min we got so far from the recursion we add it to the strip + +```swift +var strip = [Point]() +var i=0, j = 0 +while i min { break } + if dist(strip[i], strip[x]) < temp + { + temp = dist(strip[i], strip[x]) + tempFirst = strip[i] + tempSecond = strip[x] + } + x+=1 + } + i+=1 + } +``` + +- Of course not every time you end up with the same shape but this is the worst case and it's rare to happen so in reality you end up with far less points valid for comparison and this is why the algorithm gets performance in addition to the sorting tricks we did. + +- Compare the points in the strip and if you find a smaller distance replace the current one with it. + + +So this is the rundown of how the algorithm works and you could see the fun little math tricks used to optimize this and we end up with O(nlogn) complexity mainly because of the sorting. + + +## See also + +See the playground to play around with the implementation of the algorithm + +[Wikipedia](https://en.wikipedia.org/wiki/Closest_pair_of_points_problem) + +*Written for Swift Algorithm Club by [Ahmed Nader](https://github.com/ahmednader42)* diff --git a/Comb Sort/Comb Sort.playground/Contents.swift b/Comb Sort/Comb Sort.playground/Contents.swift index 39e24248a..1f7723838 100644 --- a/Comb Sort/Comb Sort.playground/Contents.swift +++ b/Comb Sort/Comb Sort.playground/Contents.swift @@ -2,11 +2,7 @@ // Created by Stephen Rutstein // 7-16-2016 -import Cocoa -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif +import Foundation // Test Comb Sort with small array of ten values let array = [2, 32, 9, -1, 89, 101, 55, -10, -12, 67] diff --git a/Comb Sort/README.markdown b/Comb Sort/README.markdown index d30d429b3..20c939307 100644 --- a/Comb Sort/README.markdown +++ b/Comb Sort/README.markdown @@ -64,8 +64,8 @@ This will sort the values of the array into ascending order -- increasing in val ## Performance Comb Sort was created to improve upon the worst case time complexity of Bubble Sort. With Comb -Sort, the worst case scenario for performance is exponential -- O(n^2). At best though, Comb Sort -performs at O(n logn) time complexity. This creates a drastic improvement over Bubble Sort's performance. +Sort, the worst case scenario for performance is polynomial -- O(n^2). At best though, Comb Sort +performs at O(n logn) time complexity -- loglinear. This creates a drastic improvement over Bubble Sort's performance. Similar to Bubble Sort, the space complexity for Comb Sort is constant -- O(1). This is extremely space efficient as it sorts the array in place. diff --git a/Comb Sort/Tests/CombSortTests.swift b/Comb Sort/Tests/CombSortTests.swift index e972c0a57..1d83af002 100644 --- a/Comb Sort/Tests/CombSortTests.swift +++ b/Comb Sort/Tests/CombSortTests.swift @@ -9,25 +9,20 @@ import XCTest class CombSortTests: XCTestCase { - var sequence: [Int]! - let expectedSequence: [Int] = [-12, -10, -1, 2, 9, 32, 55, 67, 89, 101] + var sequence: [Int]! + let expectedSequence: [Int] = [-12, -10, -1, 2, 9, 32, 55, 67, 89, 101] + + override func setUp() { + super.setUp() + sequence = [2, 32, 9, -1, 89, 101, 55, -10, -12, 67] + } - func testSwift4(){ - #if swift(>=4.0) - print("Hello, Swift 4!") - #endif - } - override func setUp() { - super.setUp() - sequence = [2, 32, 9, -1, 89, 101, 55, -10, -12, 67] - } + override func tearDown() { + super.tearDown() + } - override func tearDown() { - super.tearDown() - } - - func testCombSort() { - let sortedSequence = combSort(sequence) - XCTAssertEqual(sortedSequence, expectedSequence) - } + func testCombSort() { + let sortedSequence = combSort(sequence) + XCTAssertEqual(sortedSequence, expectedSequence) + } } diff --git a/Comb Sort/Tests/Tests.xcodeproj/project.pbxproj b/Comb Sort/Tests/Tests.xcodeproj/project.pbxproj index 7530acbdc..4582b3f2d 100644 --- a/Comb Sort/Tests/Tests.xcodeproj/project.pbxproj +++ b/Comb Sort/Tests/Tests.xcodeproj/project.pbxproj @@ -82,7 +82,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0820; - LastUpgradeCheck = 0820; + LastUpgradeCheck = 1000; ORGANIZATIONNAME = "Swift Algorithm Club"; TargetAttributes = { 056E92741E2483D300B30F52 = { @@ -142,15 +142,23 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = 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_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -179,7 +187,7 @@ SDKROOT = macosx; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; }; name = Debug; }; @@ -192,15 +200,23 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = 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_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -221,7 +237,7 @@ MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; }; name = Release; }; @@ -235,7 +251,7 @@ PRODUCT_BUNDLE_IDENTIFIER = "Swift-Algorithm-Club.Tests"; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; }; name = Debug; }; @@ -248,7 +264,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = "Swift-Algorithm-Club.Tests"; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; }; name = Release; }; diff --git a/Comb Sort/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme b/Comb Sort/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme index 55e697ad3..48faa2df7 100644 --- a/Comb Sort/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme +++ b/Comb Sort/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme @@ -1,6 +1,6 @@ (_ a: [T], _ n: Int) { - if n == 0 { - print(a) // display the current permutation - } else { - var a = a - permuteWirth(a, n - 1) - for i in 0.. + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Convex Hull/Convex Hull.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme b/Convex Hull/Convex Hull.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme index 1f93a0c4f..8f04a0f51 100644 --- a/Convex Hull/Convex Hull.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme +++ b/Convex Hull/Convex Hull.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme @@ -1,6 +1,6 @@ =4.0) - print("Hello, Swift 4!") - #endif - - generatePoints() + generateRandomPoints() quickHull(points: points) } @@ -32,11 +24,11 @@ class View: UIView { fatalError("init(coder:) has not been implemented") } - func generatePoints() { + func generateRandomPoints() { for _ in 0..=4.0) - print("Hello, Swift 4!") -#endif -func countOccurrencesOfKey(_ key: Int, inArray a: [Int]) -> Int { - func leftBoundary() -> Int { +func countOccurrences(of key: T, in array: [T]) -> Int { + var leftBoundary: Int { var low = 0 - var high = a.count + var high = array.count while low < high { let midIndex = low + (high - low)/2 - if a[midIndex] < key { + if array[midIndex] < key { low = midIndex + 1 } else { high = midIndex @@ -18,12 +14,12 @@ func countOccurrencesOfKey(_ key: Int, inArray a: [Int]) -> Int { return low } - func rightBoundary() -> Int { + var rightBoundary: Int { var low = 0 - var high = a.count + var high = array.count while low < high { let midIndex = low + (high - low)/2 - if a[midIndex] > key { + if array[midIndex] > key { high = midIndex } else { low = midIndex + 1 @@ -32,13 +28,13 @@ func countOccurrencesOfKey(_ key: Int, inArray a: [Int]) -> Int { return low } - return rightBoundary() - leftBoundary() + return rightBoundary - leftBoundary } // Simple test let a = [ 0, 1, 1, 3, 3, 3, 3, 6, 8, 10, 11, 11 ] -countOccurrencesOfKey(3, inArray: a) +countOccurrences(of: 3, in: a) // Test with arrays of random size and contents (see debug output) @@ -63,6 +59,6 @@ for _ in 0..<10 { // Note: we also test -1 and 6 to check the edge cases. for k in -1...6 { - print("\t\(k): \(countOccurrencesOfKey(k, inArray: a))") + print("\t\(k): \(countOccurrences(of: k, in: a))") } } diff --git a/Count Occurrences/CountOccurrences.swift b/Count Occurrences/CountOccurrences.swift index 10dd12c4f..532e3810e 100644 --- a/Count Occurrences/CountOccurrences.swift +++ b/Count Occurrences/CountOccurrences.swift @@ -1,14 +1,15 @@ -/* - Counts the number of times a value appears in an array in O(lg n) time. - The array must be sorted from low to high. -*/ -func countOccurrencesOfKey(_ key: Int, inArray a: [Int]) -> Int { - func leftBoundary() -> Int { +/// Counts the number of times a value appears in an array in O(log n) time. The array must be sorted from low to high. +/// +/// - Parameter key: the key to be searched for in the array +/// - Parameter array: the array to search +/// - Returns: the count of occurences of the key in the given array +func countOccurrences(of key: T, in array: [T]) -> Int { + var leftBoundary: Int { var low = 0 - var high = a.count + var high = array.count while low < high { let midIndex = low + (high - low)/2 - if a[midIndex] < key { + if array[midIndex] < key { low = midIndex + 1 } else { high = midIndex @@ -17,12 +18,12 @@ func countOccurrencesOfKey(_ key: Int, inArray a: [Int]) -> Int { return low } - func rightBoundary() -> Int { + var rightBoundary: Int { var low = 0 - var high = a.count + var high = array.count while low < high { let midIndex = low + (high - low)/2 - if a[midIndex] > key { + if array[midIndex] > key { high = midIndex } else { low = midIndex + 1 @@ -31,5 +32,5 @@ func countOccurrencesOfKey(_ key: Int, inArray a: [Int]) -> Int { return low } - return rightBoundary() - leftBoundary() + return rightBoundary - leftBoundary } diff --git a/Count Occurrences/README.markdown b/Count Occurrences/README.markdown index 85b77d2d0..8e935ea58 100644 --- a/Count Occurrences/README.markdown +++ b/Count Occurrences/README.markdown @@ -22,10 +22,10 @@ The trick is to use two binary searches, one to find where the `3`s start (the l In code this looks as follows: ```swift -func countOccurrencesOfKey(_ key: Int, inArray a: [Int]) -> Int { - func leftBoundary() -> Int { +func countOccurrences(of key: T, in array: [T]) -> Int { + var leftBoundary: Int { var low = 0 - var high = a.count + var high = array.count while low < high { let midIndex = low + (high - low)/2 if a[midIndex] < key { @@ -37,9 +37,9 @@ func countOccurrencesOfKey(_ key: Int, inArray a: [Int]) -> Int { return low } - func rightBoundary() -> Int { + var rightBoundary: Int { var low = 0 - var high = a.count + var high = array.count while low < high { let midIndex = low + (high - low)/2 if a[midIndex] > key { @@ -51,18 +51,18 @@ func countOccurrencesOfKey(_ key: Int, inArray a: [Int]) -> Int { return low } - return rightBoundary() - leftBoundary() + return rightBoundary - leftBoundary } ``` -Notice that the helper functions `leftBoundary()` and `rightBoundary()` are very similar to the [binary search](../Binary%20Search/) algorithm. The big difference is that they don't stop when they find the search key, but keep going. +Notice that the variables `leftBoundary` and `rightBoundary` are very similar to the [binary search](../Binary%20Search/) algorithm. The big difference is that they don't stop when they find the search key, but keep going. Also, notice that we constrain the type `T` to be Comparable so that the algorithm can be applied to an array of Strings, Ints or other types that conform to the Swift Comparable protocol. To test this algorithm, copy the code to a playground and then do: ```swift let a = [ 0, 1, 1, 3, 3, 3, 3, 6, 8, 10, 11, 11 ] -countOccurrencesOfKey(3, inArray: a) // returns 4 +countOccurrences(of: 3, in: a) // returns 4 ``` > **Remember:** If you use your own array, make sure it is sorted first! @@ -113,7 +113,7 @@ The right boundary is at index 7. The difference between the two boundaries is 7 Each binary search took 4 steps, so in total this algorithm took 8 steps. Not a big gain on an array of only 12 items, but the bigger the array, the more efficient this algorithm becomes. For a sorted array with 1,000,000 items, it only takes 2 x 20 = 40 steps to count the number of occurrences for any particular value. -By the way, if the value you're looking for is not in the array, then `rightBoundary()` and `leftBoundary()` return the same value and so the difference between them is 0. +By the way, if the value you're looking for is not in the array, then `rightBoundary` and `leftBoundary` return the same value and so the difference between them is 0. This is an example of how you can modify the basic binary search to solve other algorithmic problems as well. Of course, it does require that the array is sorted. diff --git a/Counting Sort/CountingSort.playground/Contents.swift b/Counting Sort/CountingSort.playground/Contents.swift index 4d918e384..dc375512a 100644 --- a/Counting Sort/CountingSort.playground/Contents.swift +++ b/Counting Sort/CountingSort.playground/Contents.swift @@ -1,10 +1,5 @@ //: Playground - noun: a place where people can play -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - enum CountingSortError: Error { case arrayEmpty } diff --git a/Depth-First Search/DepthFirstSearch.playground/Edge.o b/Depth-First Search/DepthFirstSearch.playground/Edge.o new file mode 100644 index 000000000..ab1e89b3a Binary files /dev/null and b/Depth-First Search/DepthFirstSearch.playground/Edge.o differ diff --git a/Depth-First Search/DepthFirstSearch.playground/Graph.o b/Depth-First Search/DepthFirstSearch.playground/Graph.o new file mode 100644 index 000000000..dde35fb0f Binary files /dev/null and b/Depth-First Search/DepthFirstSearch.playground/Graph.o differ diff --git a/Depth-First Search/DepthFirstSearch.playground/Node.o b/Depth-First Search/DepthFirstSearch.playground/Node.o new file mode 100644 index 000000000..1090b3a9c Binary files /dev/null and b/Depth-First Search/DepthFirstSearch.playground/Node.o differ diff --git a/Depth-First Search/DepthFirstSearch.playground/Pages/Simple Example.xcplaygroundpage/Contents.swift b/Depth-First Search/DepthFirstSearch.playground/Pages/Simple Example.xcplaygroundpage/Contents.swift index 99f20a729..c83f52af8 100644 --- a/Depth-First Search/DepthFirstSearch.playground/Pages/Simple Example.xcplaygroundpage/Contents.swift +++ b/Depth-First Search/DepthFirstSearch.playground/Pages/Simple Example.xcplaygroundpage/Contents.swift @@ -1,7 +1,4 @@ -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif +// last checked with Xcode 10.1 func depthFirstSearch(_ graph: Graph, source: Node) -> [String] { var nodesExplored = [source.label] diff --git a/Depth-First Search/DepthFirstSearch.playground/Sources/Graph.swift b/Depth-First Search/DepthFirstSearch.playground/Sources/Graph.swift index 34ee2fa3a..48b8feed5 100644 --- a/Depth-First Search/DepthFirstSearch.playground/Sources/Graph.swift +++ b/Depth-First Search/DepthFirstSearch.playground/Sources/Graph.swift @@ -5,6 +5,7 @@ public class Graph: CustomStringConvertible, Equatable { self.nodes = [] } + @discardableResult public func addNode(_ label: String) -> Node { let node = Node(label) nodes.append(node) diff --git a/Depth-First Search/Tests/Tests.xcodeproj/project.pbxproj b/Depth-First Search/Tests/Tests.xcodeproj/project.pbxproj index 8f6058600..2b433bd96 100644 --- a/Depth-First Search/Tests/Tests.xcodeproj/project.pbxproj +++ b/Depth-First Search/Tests/Tests.xcodeproj/project.pbxproj @@ -234,7 +234,7 @@ PRODUCT_BUNDLE_IDENTIFIER = swift.algorithm.club.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; }; name = Debug; }; @@ -247,7 +247,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = swift.algorithm.club.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; }; name = Release; }; diff --git a/Deque/Deque-Simple.swift b/Deque/Deque-Simple.swift index f4f5f0409..fc0fcb2b2 100644 --- a/Deque/Deque-Simple.swift +++ b/Deque/Deque-Simple.swift @@ -21,7 +21,7 @@ public struct Deque { } public mutating func enqueueFront(_ element: T) { - array.insert(element, atIndex: 0) + array.insert(element, at: 0) } public mutating func dequeue() -> T? { diff --git a/Deque/Deque.playground/Contents.swift b/Deque/Deque.playground/Contents.swift index df9b57a5e..9e7b22c02 100644 --- a/Deque/Deque.playground/Contents.swift +++ b/Deque/Deque.playground/Contents.swift @@ -1,10 +1,5 @@ //: Playground - noun: a place where people can play -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - public struct Deque { private var array = [T]() diff --git a/Deque/README.markdown b/Deque/README.markdown index fda1de5f8..f3172e16e 100644 --- a/Deque/README.markdown +++ b/Deque/README.markdown @@ -23,7 +23,7 @@ public struct Deque { } public mutating func enqueueFront(_ element: T) { - array.insert(element, atIndex: 0) + array.insert(element, at: 0) } public mutating func dequeue() -> T? { diff --git a/Dijkstra Algorithm/Dijkstra.playground/Contents.swift b/Dijkstra Algorithm/Dijkstra.playground/Contents.swift index 1cd1254b1..7c7f1a4b0 100644 --- a/Dijkstra Algorithm/Dijkstra.playground/Contents.swift +++ b/Dijkstra Algorithm/Dijkstra.playground/Contents.swift @@ -1,11 +1,6 @@ //: Playground - noun: a place where people can play import Foundation -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - var vertices: Set = Set() func createNotConnectedVertices() { diff --git a/Dijkstra Algorithm/Dijkstra.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Dijkstra Algorithm/Dijkstra.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Dijkstra Algorithm/Dijkstra.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Dijkstra Algorithm/VisualizedDijkstra.playground/Contents.swift b/Dijkstra Algorithm/VisualizedDijkstra.playground/Contents.swift index 8e61ef49b..de6b49834 100644 --- a/Dijkstra Algorithm/VisualizedDijkstra.playground/Contents.swift +++ b/Dijkstra Algorithm/VisualizedDijkstra.playground/Contents.swift @@ -9,11 +9,6 @@ import UIKit import PlaygroundSupport -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - /*: First of all, let's set up colors for our window and graph. The visited color will be applied to visited vertices. The checking color will be applied to an edge and an edge neighbor every time the algorithm is checking some vertex neighbors. And default colors are just initial colors for elements. */ diff --git a/Dijkstra Algorithm/VisualizedDijkstra.playground/Sources/Window.swift b/Dijkstra Algorithm/VisualizedDijkstra.playground/Sources/Window.swift index 73c512e6b..9a169b8c7 100644 --- a/Dijkstra Algorithm/VisualizedDijkstra.playground/Sources/Window.swift +++ b/Dijkstra Algorithm/VisualizedDijkstra.playground/Sources/Window.swift @@ -134,7 +134,7 @@ public class Window: UIView, GraphDelegate { let origin = CGPoint(x: center.x - 25, y: 100) let activityIndicatorFrame = CGRect(origin: origin, size: size) activityIndicator = UIActivityIndicatorView(frame: activityIndicatorFrame) - activityIndicator.activityIndicatorViewStyle = .whiteLarge + activityIndicator.style = .whiteLarge } @objc private func createGraphButtonTap() { diff --git a/Dijkstra Algorithm/VisualizedDijkstra.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Dijkstra Algorithm/VisualizedDijkstra.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Dijkstra Algorithm/VisualizedDijkstra.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/DiningPhilosophers/DiningPhilosophers.xcodeproj/project.pbxproj b/DiningPhilosophers/DiningPhilosophers.xcodeproj/project.pbxproj index ecb060026..7699219de 100755 --- a/DiningPhilosophers/DiningPhilosophers.xcodeproj/project.pbxproj +++ b/DiningPhilosophers/DiningPhilosophers.xcodeproj/project.pbxproj @@ -11,6 +11,7 @@ /* End PBXBuildFile section */ /* Begin PBXFileReference section */ + 232D7939216F76F700831A74 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; OBJ_12 /* DiningPhilosophers */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; path = DiningPhilosophers; sourceTree = BUILT_PRODUCTS_DIR; }; OBJ_6 /* Package.swift */ = {isa = PBXFileReference; explicitFileType = sourcecode.swift; path = Package.swift; sourceTree = ""; }; OBJ_9 /* main.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = main.swift; sourceTree = ""; }; @@ -27,13 +28,6 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - OBJ_10 /* Tests */ = { - isa = PBXGroup; - children = ( - ); - path = Tests; - sourceTree = ""; - }; OBJ_11 /* Products */ = { isa = PBXGroup; children = ( @@ -42,15 +36,14 @@ name = Products; sourceTree = BUILT_PRODUCTS_DIR; }; - OBJ_5 /* */ = { + OBJ_5 = { isa = PBXGroup; children = ( + 232D7939216F76F700831A74 /* README.md */, OBJ_6 /* Package.swift */, OBJ_7 /* Sources */, - OBJ_10 /* Tests */, OBJ_11 /* Products */, ); - name = ""; sourceTree = ""; }; OBJ_7 /* Sources */ = { @@ -96,6 +89,11 @@ isa = PBXProject; attributes = { LastUpgradeCheck = 9999; + TargetAttributes = { + OBJ_13 = { + LastSwiftMigration = 1000; + }; + }; }; buildConfigurationList = OBJ_2 /* Build configuration list for PBXProject "DiningPhilosophers" */; compatibilityVersion = "Xcode 3.2"; @@ -104,7 +102,7 @@ knownRegions = ( en, ); - mainGroup = OBJ_5 /* */; + mainGroup = OBJ_5; productRefGroup = OBJ_11 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -139,7 +137,7 @@ SWIFT_ACTIVE_COMPILATION_CONDITIONS = SWIFT_PACKAGE; SWIFT_FORCE_DYNAMIC_LINK_STDLIB = YES; SWIFT_FORCE_STATIC_LINK_STDLIB = NO; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; TARGET_NAME = DiningPhilosophers; }; name = Debug; @@ -157,7 +155,7 @@ SWIFT_ACTIVE_COMPILATION_CONDITIONS = SWIFT_PACKAGE; SWIFT_FORCE_DYNAMIC_LINK_STDLIB = YES; SWIFT_FORCE_STATIC_LINK_STDLIB = NO; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; TARGET_NAME = DiningPhilosophers; }; name = Release; diff --git a/DiningPhilosophers/DiningPhilosophers.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/DiningPhilosophers/DiningPhilosophers.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/DiningPhilosophers/DiningPhilosophers.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/DiningPhilosophers/README.md b/DiningPhilosophers/README.md index ae84cb63b..aed9dfe59 100755 --- a/DiningPhilosophers/README.md +++ b/DiningPhilosophers/README.md @@ -44,7 +44,7 @@ Based on the Chandy and Misra's analysis, a system of preference levels is deriv # Swift implementation -This Swift 3.0 implementation of the Chandy/Misra solution is based on the GCD and Semaphore tecnique that can be built on both macOS and Linux. +This Swift 3.0 implementation of the Chandy/Misra solution is based on the GCD and Semaphore technique that can be built on both macOS and Linux. The code is based on a ForkPair struct used for holding an array of DispatchSemaphore and a Philosopher struct for associate a couple of forks to each Philosopher. @@ -57,3 +57,4 @@ A background DispatchQueue is then used to let any Philosopher run asyncrounosly Dining Philosophers on Wikipedia https://en.wikipedia.org/wiki/Dining_philosophers_problem Written for Swift Algorithm Club by Jacopo Mangiavacchi +Swift 4.2 check by Bruno Scheele diff --git a/Egg Drop Problem/EggDrop.playground/Contents.swift b/Egg Drop Problem/EggDrop.playground/Contents.swift index 0b707da98..decaacdca 100644 --- a/Egg Drop Problem/EggDrop.playground/Contents.swift +++ b/Egg Drop Problem/EggDrop.playground/Contents.swift @@ -1,7 +1,3 @@ -#if swift(>=4.0) - print("Hello, Swift 4!") -#endif - drop(numberOfEggs: 2, numberOfFloors: 2) //expected answer: 2 drop(numberOfEggs: 2, numberOfFloors: 4) //expected answer: 3 drop(numberOfEggs: 2, numberOfFloors: 6) //expected answer: 3 diff --git a/Egg Drop Problem/EggDrop.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Egg Drop Problem/EggDrop.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Egg Drop Problem/EggDrop.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Egg Drop Problem/EggDrop.playground/playground.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/Egg Drop Problem/EggDrop.playground/playground.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 000000000..0c67376eb --- /dev/null +++ b/Egg Drop Problem/EggDrop.playground/playground.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,5 @@ + + + + + diff --git a/Egg Drop Problem/README.markdown b/Egg Drop Problem/README.markdown index b2f45d09e..416984193 100644 --- a/Egg Drop Problem/README.markdown +++ b/Egg Drop Problem/README.markdown @@ -12,7 +12,7 @@ If the object is incredibly resilient, and you may need to do the testing on the ## Description -You're in a building with **m** floors and you are given **n** eggs. What is the maximum number of attempts it will take to find out the floor that breaks the egg? +You're in a building with **m** floors and you are given **n** eggs. What is the minimum number of attempts it will take to find out the floor that breaks the egg? For convenience, here are a few rules to keep in mind: diff --git a/Encode and Decode Tree/EncodeAndDecodeTree.playground/Contents.swift b/Encode and Decode Tree/EncodeAndDecodeTree.playground/Contents.swift new file mode 100644 index 000000000..44c2440d5 --- /dev/null +++ b/Encode and Decode Tree/EncodeAndDecodeTree.playground/Contents.swift @@ -0,0 +1,46 @@ +//: Playground - noun: a place where people can play + +func printTree(_ root: BinaryNode?) { + guard let root = root else { + return + } + + let leftVal = root.left == nil ? "nil" : root.left!.val + let rightVal = root.right == nil ? "nil" : root.right!.val + + print("val: \(root.val) left: \(leftVal) right: \(rightVal)") + + printTree(root.left) + printTree(root.right) +} + +let coder = BinaryNodeCoder() + +let node1 = BinaryNode("a") +let node2 = BinaryNode("b") +let node3 = BinaryNode("c") +let node4 = BinaryNode("d") +let node5 = BinaryNode("e") + +node1.left = node2 +node1.right = node3 +node3.left = node4 +node3.right = node5 + +let encodeStr = try coder.encode(node1) +print(encodeStr) +// "a b # # c d # # e # #" + + +let root: BinaryNode = coder.decode(from: encodeStr)! +print("Tree:") +printTree(root) +/* + Tree: + val: a left: b right: c + val: b left: nil right: nil + val: c left: d right: e + val: d left: nil right: nil + val: e left: nil right: nil + */ + diff --git a/Encode and Decode Tree/EncodeAndDecodeTree.playground/Sources/EncodeAndDecodeTree.swift b/Encode and Decode Tree/EncodeAndDecodeTree.playground/Sources/EncodeAndDecodeTree.swift new file mode 100644 index 000000000..10bb277f5 --- /dev/null +++ b/Encode and Decode Tree/EncodeAndDecodeTree.playground/Sources/EncodeAndDecodeTree.swift @@ -0,0 +1,95 @@ +// +// EncodeAndDecodeTree.swift +// +// +// Created by Kai Chen on 19/07/2017. +// +// + +import Foundation + +protocol BinaryNodeEncoder { + func encode(_ node: BinaryNode?) throws -> String +} + +protocol BinaryNodeDecoder { + func decode(from string: String) -> BinaryNode? +} + +public class BinaryNodeCoder: BinaryNodeEncoder, BinaryNodeDecoder { + + // MARK: Private + + private let separator: Character = "," + private let nilNode = "X" + + private func decode(from array: inout [String]) -> BinaryNode? { + guard !array.isEmpty else { + return nil + } + + let value = array.removeLast() + + guard value != nilNode, let val = value as? T else { + return nil + } + + let node = BinaryNode(val) + node.left = decode(from: &array) + node.right = decode(from: &array) + + return node + } + + // MARK: Public + + public init() {} + + public func encode(_ node: BinaryNode?) throws -> String { + var str = "" + node?.preOrderTraversal { data in + if let data = data { + let string = String(describing: data) + str.append(string) + } else { + str.append(nilNode) + } + str.append(separator) + } + + return str + } + + public func decode(from string: String) -> BinaryNode? { + var components = string.split(separator: separator).reversed().map(String.init) + return decode(from: &components) + } +} + +public class BinaryNode { + public var val: Element + public var left: BinaryNode? + public var right: BinaryNode? + + public init(_ val: Element, left: BinaryNode? = nil, right: BinaryNode? = nil) { + self.val = val + self.left = left + self.right = right + } + + public func preOrderTraversal(visit: (Element?) throws -> ()) rethrows { + try visit(val) + + if let left = left { + try left.preOrderTraversal(visit: visit) + } else { + try visit(nil) + } + + if let right = right { + try right.preOrderTraversal(visit: visit) + } else { + try visit(nil) + } + } +} diff --git a/Fizz Buzz/FizzBuzz.playground/contents.xcplayground b/Encode and Decode Tree/EncodeAndDecodeTree.playground/contents.xcplayground similarity index 100% rename from Fizz Buzz/FizzBuzz.playground/contents.xcplayground rename to Encode and Decode Tree/EncodeAndDecodeTree.playground/contents.xcplayground diff --git a/Encode and Decode Tree/EncodeAndDecodeTree.swift b/Encode and Decode Tree/EncodeAndDecodeTree.swift new file mode 100644 index 000000000..6ec8a3046 --- /dev/null +++ b/Encode and Decode Tree/EncodeAndDecodeTree.swift @@ -0,0 +1,95 @@ +// +// EncodeAndDecodeTree.swift +// +// +// Created by Kai Chen on 19/07/2017. +// +// + +import Foundation + +protocol BinaryNodeEncoder { + func encode(_ node: BinaryNode?) throws -> String +} + +protocol BinaryNodeDecoder { + func decode(from string: String) -> BinaryNode? +} + +public class BinaryNodeCoder: BinaryNodeEncoder, BinaryNodeDecoder { + + // MARK: Private + + private let separator: Character = "," + private let nilNode = "X" + + private func decode(from array: inout [String]) -> BinaryNode? { + guard !array.isEmpty else { + return nil + } + + let value = array.removeLast() + + guard value != nilNode, let val = value as? T else { + return nil + } + + let node = BinaryNode(val) + node.left = decode(from: &array) + node.right = decode(from: &array) + + return node + } + + // MARK: Public + + public init() {} + + public func encode(_ node: BinaryNode?) throws -> String { + var str = "" + node?.preOrderTraversal { data in + if let data = data { + let string = String(describing: data) + str.append(string) + } else { + str.append(nilNode) + } + str.append(separator) + } + + return str + } + + public func decode(from string: String) -> BinaryNode? { + var components = string.split(separator: separator).reversed().map(String.init) + return decode(from: &components) + } +} + +public class BinaryNode { + public var val: Element + public var left: BinaryNode? + public var right: BinaryNode? + + public init(_ val: Element, left: BinaryNode? = nil, right: BinaryNode? = nil) { + self.val = val + self.left = left + self.right = right + } + + public func preOrderTraversal(visit: (Element?) throws -> ()) rethrows { + try visit(val) + + if let left = left { + try left.preOrderTraversal(visit: visit) + } else { + try visit(nil) + } + + if let right = right { + try right.preOrderTraversal(visit: visit) + } else { + try visit(nil) + } + } +} diff --git a/Encode and Decode Tree/readme.md b/Encode and Decode Tree/readme.md new file mode 100644 index 000000000..b60e0a504 --- /dev/null +++ b/Encode and Decode Tree/readme.md @@ -0,0 +1,199 @@ +# Encode and Decode Binary Tree + +> **Note**: The prerequisite for this article is an understanding of how [binary trees](https://github.com/raywenderlich/swift-algorithm-club/tree/master/Binary%20Tree) work. + +Trees are complex structures. Unlike linear collections such as arrays or linked lists, trees are *non-linear* and each element in a tree has positional information such as the *parent-child* relationship between nodes. When you want to send a tree structure to your backend, you need to send the data of each node, and a way to represent the parent-child relationship for each node. + +Your strategy in how you choose to represent this information is called your **encoding** strategy. The opposite of that - changing your encoded data back to its original form - is your **decoding** strategy. + +There are many ways to encode a tree and decode a tree. The important thing to keep in mind is that encoding and decoding strategies are closely related. The way you choose to encode a tree directly affects how you might decode a tree. + +Encoding and decoding are synonyms to *serializing* and *deserializing* trees. + +As a reference, the following code represents the typical `Node` type of a binary tree: + +```swift +class BinaryNode { + var data: Element + var leftChild: BinaryNode? + var rightChild: BinaryNode? + + // ... (rest of the implementation) +} +``` + +Your encoding and decoding methods will reside in the `BinaryNodeEncoder` and `BinaryNodeDecoder` classes: + +```swift +class BinaryNodeCoder { + + // transforms nodes into string representation + func encode(_ node: BinaryNode) throws -> String where T: Encodable { + + } + + // transforms string into `BinaryNode` representation + func decode(from string: String) + throws -> BinaryNode where T: Decodable { + + } +} +``` + +## Encoding + +As mentioned before, there are different ways to do encoding. For no particular reason, you'll opt for the following rules: + +1. The result of the encoding will be a `String` object. +2. You'll encode using *pre-order* traversal. + +Here's an example of this operation in code: + +```swift +fileprivate extension BinaryNode { + + // 1 + func preOrderTraversal(visit: (Element?) throws -> ()) rethrows { + try visit(data) + + if let leftChild = leftChild { + try leftChild.preOrderTraversal(visit: visit) + } else { + try visit(nil) + } + + if let rightChild = rightChild { + try rightChild.preOrderTraversal(visit: visit) + } else { + try visit(nil) + } + } +} + +class BinaryNodeCoder { + + // 2 + private var separator: String { return "," } + + // 3 + private var nilNode: String { return "X" } + + // 4 + func encode(_ node: BinaryNode) -> String { + var str = "" + node.preOrderTraversal { data in + if let data = data { + let string = String(describing: data) + str.append(string) + } else { + str.append(nilNode) + } + str.append(separator) + } + return str + } + + // ... +} +``` + +Here's a high level overview of the above code: + +2. `separator` is a way to distinguish the nodes in a string. To illustrate its importance, consider the following encoded string "banana". How did the tree structure look like before encoding? Without the `separator`, you can't tell. + +3. `nilNode` is used to identify empty children. This a necesssary piece of information to retain in order to rebuild the tree later. + +4. `encode` returns a `String` representation of the `BinaryNode`. For example: "ba,nana,nil" represents a tree with two nodes - "ba" and "nana" - in pre-order format. + +## Decoding + +Your decoding strategy is the exact opposite of your encoding strategy. You'll take an encoded string, and turn it back into your binary tree. + +Your encoding strategy followed the following rules: + +1. The result of the encoding will be a `String` object. +2. You'll encode using *pre-order* traversal. + +The implementation also added a few important details: + +* node values are separated by `,` +* `nil` children are denoted by the `nil` string + +These details will shape your `decode` operation. Here's a possible implementation: + +```swift + +class BinaryNodeCoder { + + // ... + + // 1 + func decode(_ string: String) -> BinaryNode? { + let components = encoded.lazy.split(separator: separator).reversed().map(String.init) + return decode(from: components) + } + + // 2 + private func decode(from array: inout [String]) -> BinaryNode? { + guard !array.isEmpty else { return nil } + let value = array.removeLast() + guard value != "\(nilNode)" else { return nil } + + let node = AVLNode(value: value) + node.leftChild = decode(from: &array) + node.rightChild = decode(from: &array) + return node + } +} +``` + +Here's a high level overview of the above code: + +1. Takes a `String`, and uses `split` to partition the contents of `string` into an array based on the `separator` defined in the encoding step. The result is first `reversed`, and then mapped to a `String`. The `reverse` step is an optimization for the next function, allowing us to use `array.removeLast()` instead of `array.removeFirst()`. + +2. Using an array as a stack, you recursively decode each node. The array keeps track of sequence of nodes and progress. + +Here's an example output of a tree undergoing the encoding and decoding process: + +``` +Original Tree + + ┌──8423 + ┌──8391 + │ └──nil +┌──7838 +│ │ ┌──4936 +│ └──3924 +│ └──2506 +830 +│ ┌──701 +└──202 + └──169 + +Encoded tree: 830,202,169,X,X,701,X,X,7838,3924,2506,X,X,4936,X,X,8391,X,8423,X,X, + +Decoded tree + + ┌──8423 + ┌──8391 + │ └──nil +┌──7838 +│ │ ┌──4936 +│ └──3924 +│ └──2506 +830 +│ ┌──701 +└──202 + └──169 + ``` + +Notice the original tree and decoded tree are identical. + +## Further Reading & References + +- [LeetCode](https://leetcode.com/problems/serialize-and-deserialize-binary-tree/description/) + +*Written for the Swift Algorithm Club by Kai Chen & Kelvin Lau* + + + diff --git a/Fixed Size Array/FixedSizeArray.playground/Contents.swift b/Fixed Size Array/FixedSizeArray.playground/Contents.swift index 8b5bc1147..616f8443d 100644 --- a/Fixed Size Array/FixedSizeArray.playground/Contents.swift +++ b/Fixed Size Array/FixedSizeArray.playground/Contents.swift @@ -1,10 +1,5 @@ //: Playground - noun: a place where people can play -// last checked with Xcode 9.0b4 -#if swift(>=4.0) - print("Hello, Swift 4!") -#endif - /* An unordered array with a maximum size. diff --git a/Fizz Buzz/FizzBuzz.playground/Contents.swift b/Fizz Buzz/FizzBuzz.playground/Contents.swift index 37acdab61..2ab13b362 100644 --- a/Fizz Buzz/FizzBuzz.playground/Contents.swift +++ b/Fizz Buzz/FizzBuzz.playground/Contents.swift @@ -1,26 +1,23 @@ -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif +// Last checked with Xcode Version 11.4.1 (11E503a) func fizzBuzz(_ numberOfTurns: Int) { - for i in 1...numberOfTurns { - var result = "" - - if i % 3 == 0 { - result += "Fizz" + guard numberOfTurns >= 1 else { + print("Number of turns must be >= 1") + return } - - if i % 5 == 0 { - result += (result.isEmpty ? "" : " ") + "Buzz" + + for i in 1...numberOfTurns { + switch (i.isMultiple(of: 3), i.isMultiple(of: 5)) { + case (false, false): + print("\(i)") + case (true, false): + print("Fizz") + case (false, true): + print("Buzz") + case (true, true): + print("Fizz Buzz") + } } - - if result.isEmpty { - result += "\(i)" - } - - print(result) - } } -fizzBuzz(100) +fizzBuzz(15) diff --git a/Fizz Buzz/FizzBuzz.swift b/Fizz Buzz/FizzBuzz.swift index b6bb410e7..bf753e648 100644 --- a/Fizz Buzz/FizzBuzz.swift +++ b/Fizz Buzz/FizzBuzz.swift @@ -1,19 +1,21 @@ -func fizzBuzz(_ numberOfTurns: Int) { - for i in 1...numberOfTurns { - var result = "" - - if i % 3 == 0 { - result += "Fizz" - } +// Last checked with Xcode Version 11.4.1 (11E503a) - if i % 5 == 0 { - result += (result.isEmpty ? "" : " ") + "Buzz" +func fizzBuzz(_ numberOfTurns: Int) { + guard numberOfTurns >= 1 else { + print("Number of turns must be >= 1") + return } - - if result.isEmpty { - result += "\(i)" + + for i in 1...numberOfTurns { + switch (i.isMultiple(of: 3), i.isMultiple(of: 5)) { + case (false, false): + print("\(i)") + case (true, false): + print("Fizz") + case (false, true): + print("Buzz") + case (true, true): + print("Fizz Buzz") + } } - - print(result) - } -} +} \ No newline at end of file diff --git a/Fizz Buzz/README.markdown b/Fizz Buzz/README.markdown index 8c250da27..be516bd46 100644 --- a/Fizz Buzz/README.markdown +++ b/Fizz Buzz/README.markdown @@ -16,46 +16,48 @@ The modulus operator `%` is the key to solving fizz buzz. The modulus operator returns the remainder after an integer division. Here is an example of the modulus operator: -| Division | Division Result | Modulus | Modulus Result | -| ------------- | -------------------------- | --------------- | ---------------:| -| 1 / 3 | 0 with a remainder of 3 | 1 % 3 | 1 | -| 5 / 3 | 1 with a remainder of 2 | 5 % 3 | 2 | -| 16 / 3 | 5 with a remainder of 1 | 16 % 3 | 1 | +| Division | Division Result | Modulus | Modulus Result| +| ----------- | --------------------- | ------------- | :-----------: | +| 1 / 3 | 0 with a remainder of 3 | 1 % 3 | 1 | +| 5 / 3 | 1 with a remainder of 2 | 5 % 3 | 2 | +| 16 / 3 | 5 with a remainder of 1 | 16 % 3 | 1 | A common approach to determine if a number is even or odd is to use the modulus operator: -| Modulus | Result | Swift Code | Swift Code Result | Comment | -| ------------- | ---------------:| ------------------------------- | -----------------:| --------------------------------------------- | -| 6 % 2 | 0 | `let isEven = (number % 2 == 0)` | `true` | If a number is divisible by 2 it is *even* | -| 5 % 2 | 1 | `let isOdd = (number % 2 != 0)` | `true` | If a number is not divisible by 2 it is *odd* | +| Modulus | Result | Swift Code | Swift Code
Result | Comment | +| -------- | :-----:| -------------------------------- | :----------------:| --------------------------------------------- | +| 6 % 2 | 0 | `let isEven = (number % 2 == 0)` | `true` | If a number is divisible by 2 it is *even* | +| 5 % 2 | 1 | `let isOdd = (number % 2 != 0)` | `true` | If a number is not divisible by 2 it is *odd* | + +Alternatively, Swift's built in function .isMultiple(of:) can be used, i.e. 6.isMultiple(of: 2) will return true, 5.isMultiple(of: 2) will return false ## Solving fizz buzz -Now we can use the modulus operator `%` to solve fizz buzz. +Now we can use the modulus operator `%` or .isMultiple(of:) method to solve fizz buzz. Finding numbers divisible by three: -| Modulus | Modulus Result | Swift Code | Swift Code Result | -| ------- | --------------:| ------------- |------------------:| -| 1 % 3 | 1 | `1 % 3 == 0` | `false` | -| 2 % 3 | 2 | `2 % 3 == 0` | `false` | -| 3 % 3 | 0 | `3 % 3 == 0` | `true` | -| 4 % 3 | 1 | `4 % 3 == 0` | `false` | +| Modulus | Modulus
Result | Swift Code
using Modulo | Swift Code
using .isMultiple(of:) | Swift Code
Result | +| ------- | :---------------: | -------------------------- | ------------------------------------ | ------------------- | +|1 % 3 | 1 | `1 % 3 == 0` | `1.isMultiple(of: 3)` | `false` | +|2 % 3 | 2 | `2 % 3 == 0` | `2.isMultiple(of: 3)` | `false` | +|3 % 3 | 0 | `3 % 3 == 0` | `3.isMultiple(of: 3)` | `true` | +|4 % 3 | 1 | `4 % 3 == 0` | `4.isMultiple(of: 3)` | `false` | Finding numbers divisible by five: -| Modulus | Modulus Result | Swift Code | Swift Code Result | -| ------- | --------------:| ------------- |------------------:| -| 1 % 5 | 1 | `1 % 5 == 0` | `false` | -| 2 % 5 | 2 | `2 % 5 == 0` | `false` | -| 3 % 5 | 3 | `3 % 5 == 0` | `false` | -| 4 % 5 | 4 | `4 % 5 == 0` | `false` | -| 5 % 5 | 0 | `5 % 5 == 0` | `true` | -| 6 % 5 | 1 | `6 % 5 == 0` | `false` | +| Modulus | Modulus
Result | Swift Code
using Modulo | Swift Code
using .isMultiple(of:) | Swift Code
Result | +| ------- | :---------------: | -------------------------- | ------------------------------------ | -------------------- | +| 1 % 5 | 1 | `1 % 5 == 0` | `1.isMultiple(of: 5)` | `false` | +| 2 % 5 | 2 | `2 % 5 == 0` | `2.isMultiple(of: 5)` | `false` | +| 3 % 5 | 3 | `3 % 5 == 0` | `3.isMultiple(of: 5)` | `false` | +| 4 % 5 | 4 | `4 % 5 == 0` | `4.isMultiple(of: 5)` | `false` | +| 5 % 5 | 0 | `5 % 5 == 0` | `5.isMultiple(of: 5)` | `true` | +| 6 % 5 | 1 | `6 % 5 == 0` | `6.isMultiple(of: 5)` | `false` | ## The code -Here is a simple implementation in Swift: +Here is a simple implementation in Swift using Modulus approach ```swift func fizzBuzz(_ numberOfTurns: Int) { @@ -79,7 +81,31 @@ func fizzBuzz(_ numberOfTurns: Int) { } ``` -Put this code in a playground and test it like so: +Here is simple implementation in Swift using .isMultiple(of:) and switch statement + +```swift +func fizzBuzz(_ numberOfTurns: Int) { + guard numberOfTurns >= 1 else { + print("Number of turns must be >= 1") + return + } + + for i in 1...numberOfTurns { + switch (i.isMultiple(of: 3), i.isMultiple(of: 5)) { + case (false, false): + print("\(i)") + case (true, false): + print("Fizz") + case (false, true): + print("Buzz") + case (true, true): + print("Fizz Buzz") + } + } +} +``` + +Put either code in a playground and test it like so: ```swift fizzBuzz(15) @@ -87,10 +113,25 @@ fizzBuzz(15) This will output: - 1, 2, Fizz, 4, Buzz, Fizz, 7, 8, Fizz, Buzz, 11, Fizz, 13, 14, Fizz Buzz +1 +2 +Fizz +4 +Buzz +Fizz +7 +8 +Fizz +Buzz +11 +Fizz +13 +14 +Fizz Buzz ## See also [Fizz buzz on Wikipedia](https://en.wikipedia.org/wiki/Fizz_buzz) -*Written by [Chris Pilcher](https://github.com/chris-pilcher)* +*Originally written by [Chris Pilcher](https://github.com/chris-pilcher)*
+*Updated by [Lance Rettberg](https://github.com/l-rettberg)* diff --git a/GCD/GCD.playground/Contents.swift b/GCD/GCD.playground/Contents.swift index f0fceb0da..bb8d49055 100644 --- a/GCD/GCD.playground/Contents.swift +++ b/GCD/GCD.playground/Contents.swift @@ -1,44 +1,21 @@ -//: Playground - noun: a place where people can play - -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - -// Recursive version -func gcd(_ a: Int, _ b: Int) -> Int { - let r = a % b - if r != 0 { - return gcd(b, r) - } else { - return b - } -} - -/* -// Iterative version -func gcd(m: Int, _ n: Int) -> Int { - var a = 0 - var b = max(m, n) - var r = min(m, n) - - while r != 0 { - a = b - b = r - r = a % b - } - return b -} -*/ - -func lcm(_ m: Int, _ n: Int) -> Int { - return m / gcd(m, n) * n // we divide before multiplying to avoid integer overflow -} - gcd(52, 39) // 13 gcd(228, 36) // 12 gcd(51357, 3819) // 57 gcd(841, 299) // 1 -lcm(2, 3) // 6 -lcm(10, 8) // 40 +gcd(52, 39, using: gcdRecursiveEuklid) // 13 +gcd(228, 36, using: gcdRecursiveEuklid) // 12 +gcd(51357, 3819, using: gcdRecursiveEuklid) // 57 +gcd(841, 299, using: gcdRecursiveEuklid) // 1 + +gcd(52, 39, using: gcdBinaryRecursiveStein) // 13 +gcd(228, 36, using: gcdBinaryRecursiveStein) // 12 +gcd(51357, 3819, using: gcdBinaryRecursiveStein) // 57 +gcd(841, 299, using: gcdBinaryRecursiveStein) // 1 + +do { + try lcm(2, 3) // 6 + try lcm(10, 8, using: gcdRecursiveEuklid) // 40 +} catch { + dump(error) +} diff --git a/GCD/GCD.playground/Sources/GCD.swift b/GCD/GCD.playground/Sources/GCD.swift new file mode 100644 index 000000000..4eb0c7621 --- /dev/null +++ b/GCD/GCD.playground/Sources/GCD.swift @@ -0,0 +1,143 @@ +/* + Finds the largest positive integer that divides both + m and n without a remainder. + + - Parameter m: First natural number + - Parameter n: Second natural number + - Parameter using: The used algorithm to calculate the gcd. + If nothing provided, the Iterative Euclidean + algorithm is used. + - Returns: The natural gcd of m and n. + */ +public func gcd(_ m: Int, _ n: Int, using gcdAlgorithm: (Int, Int) -> (Int) = gcdIterativeEuklid) -> Int { + return gcdAlgorithm(m, n) +} + +/* + Iterative approach based on the Euclidean algorithm. + The Euclidean algorithm is based on the principle that the greatest + common divisor of two numbers does not change if the larger number + is replaced by its difference with the smaller number. + - Parameter m: First natural number + - Parameter n: Second natural number + - Returns: The natural gcd of m and n. + */ +public func gcdIterativeEuklid(_ m: Int, _ n: Int) -> Int { + var a: Int = 0 + var b: Int = max(m, n) + var r: Int = min(m, n) + + while r != 0 { + a = b + b = r + r = a % b + } + return b +} + +/* + Recursive approach based on the Euclidean algorithm. + + - Parameter m: First natural number + - Parameter n: Second natural number + - Returns: The natural gcd of m and n. + - Note: The recursive version makes only tail recursive calls. + Most compilers for imperative languages do not optimize these. + The swift compiler as well as the obj-c compiler is able to do + optimizations for tail recursive calls, even though it still ends + up to be the same in terms of complexity. That said, tail call + elimination is not mutually exclusive to recursion. + */ +public func gcdRecursiveEuklid(_ m: Int, _ n: Int) -> Int { + let r: Int = m % n + if r != 0 { + return gcdRecursiveEuklid(n, r) + } else { + return n + } +} + +/* + The binary GCD algorithm, also known as Stein's algorithm, + is an algorithm that computes the greatest common divisor of two + nonnegative integers. Stein's algorithm uses simpler arithmetic + operations than the conventional Euclidean algorithm; it replaces + division with arithmetic shifts, comparisons, and subtraction. + + - Parameter m: First natural number + - Parameter n: Second natural number + - Returns: The natural gcd of m and n + - Complexity: worst case O(n^2), where n is the number of bits + in the larger of the two numbers. Although each step reduces + at least one of the operands by at least a factor of 2, + the subtract and shift operations take linear time for very + large integers + */ +public func gcdBinaryRecursiveStein(_ m: Int, _ n: Int) -> Int { + if let easySolution = findEasySolution(m, n) { return easySolution } + + if (m & 1) == 0 { + // m is even + if (n & 1) == 1 { + // and n is odd + return gcdBinaryRecursiveStein(m >> 1, n) + } else { + // both m and n are even + return gcdBinaryRecursiveStein(m >> 1, n >> 1) << 1 + } + } else if (n & 1) == 0 { + // m is odd, n is even + return gcdBinaryRecursiveStein(m, n >> 1) + } else if (m > n) { + // reduce larger argument + return gcdBinaryRecursiveStein((m - n) >> 1, n) + } else { + // reduce larger argument + return gcdBinaryRecursiveStein((n - m) >> 1, m) + } +} + +/* + Finds an easy solution for the gcd. + - Parameter m: First natural number + - Parameter n: Second natural number + - Returns: A natural gcd of m and n if possible. + - Note: It might be relevant for different usecases to + try finding an easy solution for the GCD calculation + before even starting more difficult operations. + */ +func findEasySolution(_ m: Int, _ n: Int) -> Int? { + if m == n { + return m + } + if m == 0 { + return n + } + if n == 0 { + return m + } + return nil +} + + +public enum LCMError: Error { + case divisionByZero +} + +/* + Calculates the lcm for two given numbers using a specified gcd algorithm. + + - Parameter m: First natural number. + - Parameter n: Second natural number. + - Parameter using: The used gcd algorithm to calculate the lcm. + If nothing provided, the Iterative Euclidean + algorithm is used. + - Throws: Can throw a `divisionByZero` error if one of the given + attributes turns out to be zero or less. + - Returns: The least common multiplier of the two attributes as + an unsigned integer + */ +public func lcm(_ m: Int, _ n: Int, using gcdAlgorithm: (Int, Int) -> (Int) = gcdIterativeEuklid) throws -> Int { + guard m & n != 0 else { throw LCMError.divisionByZero } + return m / gcdAlgorithm(m, n) * n +} diff --git a/GCD/GCD.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/GCD/GCD.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/GCD/GCD.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/GCD/GCD.playground/timeline.xctimeline b/GCD/GCD.playground/timeline.xctimeline deleted file mode 100644 index bf468afec..000000000 --- a/GCD/GCD.playground/timeline.xctimeline +++ /dev/null @@ -1,6 +0,0 @@ - - - - - diff --git a/GCD/GCD.swift b/GCD/GCD.swift deleted file mode 100644 index bd903e58a..000000000 --- a/GCD/GCD.swift +++ /dev/null @@ -1,34 +0,0 @@ -/* - Euclid's algorithm for finding the greatest common divisor -*/ -func gcd(_ m: Int, _ n: Int) -> Int { - var a = 0 - var b = max(m, n) - var r = min(m, n) - - while r != 0 { - a = b - b = r - r = a % b - } - return b -} - -/* -// Recursive version -func gcd(_ a: Int, _ b: Int) -> Int { - let r = a % b - if r != 0 { - return gcd(b, r) - } else { - return b - } -} -*/ - -/* - Returns the least common multiple of two numbers. -*/ -func lcm(_ m: Int, _ n: Int) -> Int { - return m / gcd(m, n) * n -} diff --git a/GCD/README.markdown b/GCD/README.markdown index d7e9304bd..4c459eff0 100644 --- a/GCD/README.markdown +++ b/GCD/README.markdown @@ -1,14 +1,19 @@ # Greatest Common Divisor -The *greatest common divisor* (or Greatest Common Factor) of two numbers `a` and `b` is the largest positive integer that divides both `a` and `b` without a remainder. +The *greatest common divisor* (or Greatest Common Factor) of two numbers `a` and `b` is the largest positive integer that divides both `a` and `b` without a remainder. The GCD.swift file contains three different algorithms of how to calculate the greatest common divisor. +For example, `gcd(39, 52) = 13` because 13 divides 39 (`39 / 13 = 3`) as well as 52 (`52 / 13 = 4`). But there is no larger number than 13 that divides them both. +You've probably had to learn about this in school at some point. :-) -For example, `gcd(39, 52) = 13` because 13 divides 39 (`39/13 = 3`) as well as 52 (`52/13 = 4`). But there is no larger number than 13 that divides them both. +You probably won't need to use the GCD or LCM in any real-world problems, but it's cool to play around with this ancient algorithm. It was first described by Euklid in his [Elements](http://publicdomainreview.org/collections/the-first-six-books-of-the-elements-of-euclid-1847/) around 300 BC. Rumor has it that he discovered this algorithm while he was hacking on his Commodore 64. -You've probably had to learn about this in school at some point. :-) +## Different Algorithms -The laborious way to find the GCD of two numbers is to first figure out the factors of both numbers, then take the greatest number they have in common. The problem is that factoring numbers is quite difficult, especially when they get larger. (On the plus side, that difficulty is also what keeps your online payments secure.) +This example includes three different algorithms to find the same result. + +### Iterative Euklidean -There is a smarter way to calculate the GCD: Euclid's algorithm. The big idea here is that, +The laborious way to find the GCD of two numbers is to first figure out the factors of both numbers, then take the greatest number they have in common. The problem is that factoring numbers is quite difficult, especially when they get larger. (On the plus side, that difficulty is also what keeps your online payments secure.) +There is a smarter way to calculate the GCD: Euklid's algorithm. The big idea here is that, gcd(a, b) = gcd(b, a % b) @@ -17,29 +22,58 @@ where `a % b` calculates the remainder of `a` divided by `b`. Here is an implementation of this idea in Swift: ```swift -func gcd(_ a: Int, _ b: Int) -> Int { - let r = a % b - if r != 0 { - return gcd(b, r) - } else { +func gcdIterativeEuklid(_ m: Int, _ n: Int) -> Int { + var a: Int = 0 + var b: Int = max(m, n) + var r: Int = min(m, n) + + while r != 0 { + a = b + b = r + r = a % b + } return b - } } ``` Put it in a playground and try it out with these examples: ```swift -gcd(52, 39) // 13 -gcd(228, 36) // 12 -gcd(51357, 3819) // 57 +gcd(52, 39, using: gcdIterativeEuklid) // 13 +gcd(228, 36, using: gcdIterativeEuklid) // 12 +gcd(51357, 3819, using: gcdIterativeEuklid) // 57 +``` + +### Recursive Euklidean + +Here is a slightly different implementation of Euklid's algorithm. Unlike the first version this doesn't use a `while` loop, but leverages recursion. + +```swift +func gcdRecursiveEuklid(_ m: Int, _ n: Int) -> Int { + let r: Int = m % n + if r != 0 { + return gcdRecursiveEuklid(n, r) + } else { + return n + } +} +``` + +Put it in a playground and compare it with the results of the iterative Eulidean gcd. They should return the same results: + +```swift +gcd(52, 39, using: gcdRecursiveEuklid) // 13 +gcd(228, 36, using: gcdRecursiveEuklid) // 12 +gcd(51357, 3819, using: gcdRecursiveEuklid) // 57 ``` -Let's step through the third example: +The `max()` and `min()` at the top of the function make sure we always divide the larger number by the smaller one. + +Let's step through the third example using the *recursive Euklidean algorithm* here: gcd(51357, 3819) -According to Euclid's rule, this is equivalent to, +According to Euklid's rule, this is equivalent to, gcd(3819, 51357 % 3819) = gcd(3819, 1710) @@ -47,17 +81,17 @@ because the remainder of `51357 % 3819` is `1710`. If you work out this division So `gcd(51357, 3819)` is the same as `gcd(3819, 1710)`. That's useful because we can keep simplifying: - gcd(3819, 1710) = gcd(1710, 3819 % 1710) = - gcd(1710, 399) = gcd(399, 1710 % 399) = - gcd(399, 114) = gcd(114, 399 % 114) = - gcd(114, 57) = gcd(57, 114 % 57) = + gcd(3819, 1710) = gcd(1710, 3819 % 1710) = + gcd(1710, 399) = gcd(399, 1710 % 399) = + gcd(399, 114) = gcd(114, 399 % 114) = + gcd(114, 57) = gcd(57, 114 % 57) = gcd(57, 0) And now can't divide any further. The remainder of `114 / 57` is zero because `114 = 57 * 2` exactly. That means we've found the answer: gcd(3819, 51357) = gcd(57, 0) = 57 -So in each step of Euclid's algorithm the numbers become smaller and at some point it ends when one of them becomes zero. +So in each step of Euklid's algorithm the numbers become smaller and at some point it ends when one of them becomes zero. By the way, it's also possible that two numbers have a GCD of 1. They are said to be *relatively prime*. This happens when there is no number that divides them both, for example: @@ -65,44 +99,82 @@ By the way, it's also possible that two numbers have a GCD of 1. They are said t gcd(841, 299) // 1 ``` -Here is a slightly different implementation of Euclid's algorithm. Unlike the first version this doesn't use recursion but only a basic `while` loop. +### Binary Recursive Stein + +The binary GCD algorithm, also known as Stein's algorithm, is an algorithm that computes the greatest common divisor of two nonnegative integers. Stein's algorithm is very similar to the recursive Eulidean algorithm, but uses arithmetical operations, which are simpler for computers to perform, than the conventional Euclidean algorithm does. It replaces division with arithmetic shifts, comparisons, and subtraction. ```swift -func gcd(_ m: Int, _ n: Int) -> Int { - var a = 0 - var b = max(m, n) - var r = min(m, n) - - while r != 0 { - a = b - b = r - r = a % b - } - return b +func gcdBinaryRecursiveStein(_ m: Int, _ n: Int) -> Int { + if let easySolution = findEasySolution(m, n) { return easySolution } + + if (m & 1) == 0 { + // m is even + if (n & 1) == 1 { + // and n is odd + return gcdBinaryRecursiveStein(m >> 1, n) + } else { + // both m and n are even + return gcdBinaryRecursiveStein(m >> 1, n >> 1) << 1 + } + } else if (n & 1) == 0 { + // m is odd, n is even + return gcdBinaryRecursiveStein(m, n >> 1) + } else if (m > n) { + // reduce larger argument + return gcdBinaryRecursiveStein((m - n) >> 1, n) + } else { + // reduce larger argument + return gcdBinaryRecursiveStein((n - m) >> 1, m) + } } ``` -The `max()` and `min()` at the top of the function make sure we always divide the larger number by the smaller one. +Depending on your application and your input expectations, it might be reasonable to also search for an "easy solution" using the other gcd implementations: -## Least Common Multiple +```swift +func findEasySolution(_ m: Int, _ n: Int) -> Int? { + if m == n { + return m + } + if m == 0 { + return n + } + if n == 0 { + return m + } + return nil +} -An idea related to the GCD is the *least common multiple* or LCM. +``` -The least common multiple of two numbers `a` and `b` is the smallest positive integer that is a multiple of both. In other words, the LCM is evenly divisible by `a` and `b`. +Put it in a playground and compare it with the results of the other gcd implementations: -For example: `lcm(2, 3) = 6` because 6 can be divided by 2 and also by 3. +```swift +gcd(52, 39, using: gcdBinaryRecursiveStein) // 13 +gcd(228, 36, using: gcdBinaryRecursiveStein) // 12 +gcd(51357, 3819, using: gcdBinaryRecursiveStein) // 57 +``` + +### Least Common Multiple + +Another algorithm related to the GCD is the *least common multiple* or LCM. + +The least common multiple of two numbers `a` and `b` is the smallest positive integer that is a multiple of both. In other words, the LCM is evenly divisible by `a` and `b`. The example implementation of the LCM takes two numbers and an optional specification which GCD algorithm is used. -We can calculate the LCM using Euclid's algorithm too: +For example: `lcm(2, 3, using: gcdRecursiveEuklid) = 6` , which tells us that 6 is the smallest number that can be devided by 2 as well as 3. - a * b - lcm(a, b) = --------- - gcd(a, b) +We can calculate the LCM using Euklid's algorithm too: + +a * b +lcm(a, b) = --------- +gcd(a, b) In code: ```swift -func lcm(_ m: Int, _ n: Int) -> Int { - return m / gcd(m, n) * n +func lcm(_ m: Int, _ n: Int, using gcdAlgorithm: (Int, Int) -> (Int) = gcdIterativeEuklid) throws -> Int { +guard (m & n) != 0 else { throw LCMError.divisionByZero } +return m / gcdAlgorithm(m, n) * n } ``` @@ -112,6 +184,11 @@ And to try it out in a playground: lcm(10, 8) // 40 ``` -You probably won't need to use the GCD or LCM in any real-world problems, but it's cool to play around with this ancient algorithm. It was first described by Euclid in his [Elements](http://publicdomainreview.org/collections/the-first-six-books-of-the-elements-of-euclid-1847/) around 300 BC. Rumor has it that he discovered this algorithm while he was hacking on his Commodore 64. +## Discussion + +While these algorithms all calculate the same result, comparing their plane complexity might not be enough to decide for one of them, though. The original iterative Euklidean algorithm is easier to understand. The recursive Euklidean and Stein's algorithm, while being generally faster, their runtime is heavily dependend on the environment they are running on. +_If a fast calculation of the gcd is necessary, a runtime comparison for the specific platform and compiler optimization level should be done for the rekursive Euklidean and Stein's algorithm._ *Written for Swift Algorithm Club by Matthijs Hollemans* + +*Extended by Simon C. Krüger* diff --git a/Genetic/README.markdown b/Genetic/README.markdown new file mode 100644 index 000000000..5dc4747f2 --- /dev/null +++ b/Genetic/README.markdown @@ -0,0 +1,312 @@ +# Genetic Algorthim + +## What is it? + +A genetic algorithm (GA) is process inspired by natural selection to find high quality solutions. Most commonly used for optimization. GAs rely on the bio-inspired processes of natural selection, more specifically the process of selection (fitness), crossover and mutation. To understand more, let's walk through these processes in terms of biology: + +### Selection +>**Selection**, in biology, the preferential survival and reproduction or preferential elimination of individuals with certain genotypes (genetic compositions), by means of natural or artificial controlling factors. + +In other words, survival of the fittest. Organisms that survive in their environment tend to reproduce more. With GAs we generate a fitness model that will rank individuals and give them a better chance for reproduction. + +### Crossover +>**Chromosomal crossover** (or crossing over) is the exchange of genetic material between homologous chromosomes that results in recombinant chromosomes during sexual reproduction [Wikipedia](https://en.wikipedia.org/wiki/Chromosomal_crossover) + +Simply reproduction. A generation will be a mixed representation of the previous generation, with offspring taking DNA from both parents. GAs do this by randomly, but weightily, mating offspring to create new generations. + +### Mutation +>**Mutation**, an alteration in the genetic material (the genome) of a cell of a living organism or of a virus that is more or less permanent and that can be transmitted to the cell’s or the virus’s descendants. [Britannica](https://www.britannica.com/science/mutation-genetics) + +The randomization that allows for organisms to change over time. In GAs we build a randomization process that will mutate offspring in a population in order to introduce fitness variance. + +### Resources: +* [Genetic Algorithms in Search Optimization, and Machine Learning](https://www.amazon.com/Genetic-Algorithms-Optimization-Machine-Learning/dp/0201157675/ref=sr_1_sc_1?ie=UTF8&qid=1520628364&sr=8-1-spell&keywords=Genetic+Algortithms+in+search) +* [Wikipedia](https://en.wikipedia.org/wiki/Genetic_algorithm) +* [My Original Gist](https://gist.github.com/blainerothrock/efda6e12fe10792c99c990f8ff3daeba) + +## The Code + +### Problem +For this quick and dirty example, we are going to produce an optimized string using a simple genetic algorithm. More specifically we are trying to take a randomly generated origin string of a fixed length and evolve it into the most optimized string of our choosing. + +We will be creating a bio-inspired world where the absolute existence is the string `Hello, World!`. Nothing in this universe is better and it's our goal to get as close to it as possible to ensure survival. + +### Define the Universe + +Before we dive into the core processes we need to set up our "universe". First let's define a lexicon, a set of everything that exists in our universe. + +```swift +let lex: [UInt8] = " !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~".asciiArray +``` + +To make things easier, we are actually going to work in [Unicode values](https://en.wikipedia.org/wiki/List_of_Unicode_characters), so let's define a String extension to help with that. + +```swift +extension String { + var unicodeArray: [UInt8] { + return [UInt8](self.utf8) + } +} +``` + + Now, let's define a few global variables for the universe: + * `OPTIMAL`: This is the end goal and what we will be using to rate fitness. In the real world this will not exist + * `DNA_SIZE`: The length of the string in our population. Organisms need to be similar + * `POP_SIZE`: Size of each generation + * `MAX_GENERATIONS`: Max number of generations, script will stop when it reach 5000 if the optimal value is not found + * `MUTATION_CHANCE`: The chance in which a random nucleotide can mutate (`1/MUTATION_CHANCE`) + + ```swift +let OPTIMAL:[UInt8] = "Hello, World".unicodeArray +let DNA_SIZE = OPTIMAL.count +let POP_SIZE = 50 +let GENERATIONS = 5000 +let MUTATION_CHANCE = 100 + ``` + + ### Population Zero + +Before selecting, crossover and mutation, we need a population to start with. Now that we have the universe defined we can write that function: + + ```swift + func randomPopulation(from lexicon: [UInt8], populationSize: Int, dnaSize: Int) -> [[UInt8]] { + guard lexicon.count > 1 else { return [] } + var pop = [[UInt8]]() + + (0.. Int { + guard dna.count == optimal.count else { return -1 } + var fitness = 0 + for index in dna.indices { + fitness += abs(Int(dna[index]) - Int(optimal[index])) + } + return fitness +} +``` + +The above will produce a fitness value to an individual. The perfect solution, "Hello, World" will have a fitness of 0. "Gello, World" will have a fitness of 1 since it is one unicode value off from the optimal (`H->G`). + +This example is very simple, but it'll work for our example. In a real world problem, the optimal solution is unknown or impossible. [Here](https://iccl.inf.tu-dresden.de/w/images/b/b7/GA_for_TSP.pdf) is a paper about optimizing a solution for the famous [traveling salesman problem](https://en.wikipedia.org/wiki/Travelling_salesman_problem) using a GA. In this example the problem is unsolvable by modern computers, but you can rate a individual solution by distance traveled. The optimal fitness here is an impossible 0. The closer the solution is to 0, the better chance for survival. In our example we will reach our goal, a fitness of 0. + +The second part to selection is weighted choice, also called roulette wheel selection. This defines how individuals are selected for the reproduction process out of the current population. Just because you are the best choice for natural selection doesn't mean the environment will select you. The individual could fall off a cliff, get dysentery or be unable to reproduce. + +Let's take a second and ask why on this one. Why would you not always want to select the most fit from a population? It's hard to see from this simple example, but let's think about dog breeding, because breeders remove this process and hand select dogs for the next generation. As a result you get improved desired characteristics, but the individuals will also continue to carry genetic disorders that come along with those traits. A certain "branch" of evolution may beat out the current fittest solution at a later time. This may be ok depending on the problem, but to keep this educational we will go with the bio-inspired way. + +With all that, here is our weight choice function: + +func weightedChoice(items: [(dna: [UInt8], weight: Double)]) -> (dna: [UInt8], weight: Double) { + + let total = items.reduce(0) { $0 + $1.weight } + var n = Double.random(in: 0..<(total * 1000000)) / 1000000.0 + + for item in items { + if n < item.weight { + return item + } + n = n - item.weight + } + return items[1] +} + + +The above function takes a list of individuals with their calculated fitness. Then selects one at random offset by their fitness value. The horrible 1,000,000 multiplication and division is to insure precision by calculating decimals. `Double.random` only uses integers so this is required to convert to a precise Double, it's not perfect, but enough for our example. + +## Mutation + +The all powerful mutation, the thing that introduces otherwise non existent fitness variance. It can either hurt of improve a individuals fitness but over time it will cause evolution towards more fit populations. Imagine if our initial random population was missing the charachter `H`, in that case we need to rely on mutation to introduce that character into the population in order to achieve the optimal solution. + +```swift +func mutate(lexicon: [UInt8], dna: [UInt8], mutationChance: Int) -> [UInt8] { + var outputDna = dna + (0.. [UInt8] { + let pos = Int.random(in: 0..?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~".unicodeArray + +// This is the end goal and what we will be using to rate fitness. In the real world this will not exist +let OPTIMAL:[UInt8] = "Hello, World".unicodeArray + +// The length of the string in our population. Organisms need to be similar +let DNA_SIZE = OPTIMAL.count + +// Size of each generation +let POP_SIZE = 50 + +// Max number of generations, script will stop when it reaches 5000 if the optimal value is not found +let GENERATIONS = 5000 + +// The chance in which a random nucleotide can mutate (1/n) +let MUTATION_CHANCE = 100 + +func randomPopulation(from lexicon: [UInt8], populationSize: Int, dnaSize: Int) -> [[UInt8]] { + var pop = [[UInt8]]() + + (0.. Int { + var fitness = 0 + for index in dna.indices { + fitness += abs(Int(dna[index]) - Int(optimal[index])) + } + return fitness +} + +func weightedChoice(items: [(dna: [UInt8], weight: Double)]) -> (dna: [UInt8], weight: Double) { + + let total = items.reduce(0) { $0 + $1.weight } + var n = Double.random(in: 0..<(total * 1000000)) / 1000000.0 + + for item in items { + if n < item.weight { + return item + } + n = n - item.weight + } + return items[1] +} + +func mutate(lexicon: [UInt8], dna: [UInt8], mutationChance: Int) -> [UInt8] { + var outputDna = dna + (0.. [UInt8] { + let pos = Int.random(in: 0.. + + + + diff --git a/Genetic/gen.swift b/Genetic/gen.swift new file mode 100644 index 000000000..a61e61785 --- /dev/null +++ b/Genetic/gen.swift @@ -0,0 +1,148 @@ +//: Playground - noun: a place where people can play + +import Foundation + +extension String { + var unicodeArray: [UInt8] { + return [UInt8](self.utf8) + } +} + + +let lex: [UInt8] = " !\"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~".unicodeArray + +// This is the end goal and what we will be using to rate fitness. In the real world this will not exist +let OPTIMAL:[UInt8] = "Hello, World".unicodeArray + +// The length of the string in our population. Organisms need to be similar +let DNA_SIZE = OPTIMAL.count + +// size of each generation +let POP_SIZE = 50 + +// max number of generations, script will stop when it reach 5000 if the optimal value is not found +let MAX_GENERATIONS = 5000 + +// The chance in which a random nucleotide can mutate (1/n) +let MUTATION_CHANCE = 100 + +func randomChar(from lexicon: [UInt8]) -> UInt8 { + let len = UInt32(lexicon.count-1) + let rand = Int(arc4random_uniform(len)) + return lexicon[rand] +} + +func randomPopulation(from lexicon: [UInt8], populationSize: Int, dnaSize: Int) -> [[UInt8]] { + + var pop = [[UInt8]]() + + (0.. Int { + var fitness = 0 + (0...dna.count-1).forEach { c in + fitness += abs(Int(dna[c]) - Int(optimal[c])) + } + return fitness +} + +func weightedChoice(items:[(dna:[UInt8], weight:Double)]) -> (dna:[UInt8], weight:Double) { + + let total = items.reduce(0.0) { return $0 + $1.weight} + + var n = Double(arc4random_uniform(UInt32(total * 1000000.0))) / 1000000.0 + + for item in items { + if n < item.weight { + return item + } + n = n - item.weight + } + return items[1] +} + +func mutate(lexicon: [UInt8], dna:[UInt8], mutationChance:Int) -> [UInt8] { + var outputDna = dna + + (0.. [UInt8] { + let pos = Int(arc4random_uniform(UInt32(dnaSize-1))) + + let dna1Index1 = dna1.index(dna1.startIndex, offsetBy: pos) + let dna2Index1 = dna2.index(dna2.startIndex, offsetBy: pos) + + return [UInt8](dna1.prefix(upTo: dna1Index1) + dna2.suffix(from: dna2Index1)) +} + +func main() { + + // generate the starting random population + var population:[[UInt8]] = randomPopulation(from: lex, populationSize: POP_SIZE, dnaSize: DNA_SIZE) + // print("population: \(population), dnaSize: \(DNA_SIZE) ") + var fittest = [UInt8]() + + for generation in 0...MAX_GENERATIONS { + + var weightedPopulation = [(dna:[UInt8], weight:Double)]() + + // calulcated the fitness of each individual in the population + // and add it to the weight population (weighted = 1.0/fitness) + for individual in population { + let fitnessValue = calculateFitness(dna: individual, optimal: OPTIMAL) + + let pair = ( individual, fitnessValue == 0 ? 1.0 : Double(100/POP_SIZE)/Double( fitnessValue ) ) + + weightedPopulation.append(pair) + } + + population = [] + + // create a new generation using the individuals in the origional population + (0...POP_SIZE).forEach { _ in + let ind1 = weightedChoice(items: weightedPopulation) + let ind2 = weightedChoice(items: weightedPopulation) + + let offspring = crossover(dna1: ind1.dna, dna2: ind2.dna, dnaSize: DNA_SIZE) + + // append to the population and mutate + population.append(mutate(lexicon: lex, dna: offspring, mutationChance: MUTATION_CHANCE)) + } + + fittest = population[0] + var minFitness = calculateFitness(dna: fittest, optimal: OPTIMAL) + + // parse the population for the fittest string + population.forEach { indv in + let indvFitness = calculateFitness(dna: indv, optimal: OPTIMAL) + if indvFitness < minFitness { + fittest = indv + minFitness = indvFitness + } + } + if minFitness == 0 { break; } + print("\(generation): \(String(bytes: fittest, encoding: .utf8)!)") + + } + print("fittest string: \(String(bytes: fittest, encoding: .utf8)!)") +} + +main() diff --git a/Graph/Graph.playground/Contents.swift b/Graph/Graph.playground/Contents.swift index 0410942f8..fde396e64 100644 --- a/Graph/Graph.playground/Contents.swift +++ b/Graph/Graph.playground/Contents.swift @@ -1,11 +1,5 @@ import Graph -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - - for graph in [AdjacencyMatrixGraph(), AdjacencyListGraph()] { let v1 = graph.createVertex(1) diff --git a/Graph/Graph.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Graph/Graph.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Graph/Graph.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Graph/Graph.playground/timeline.xctimeline b/Graph/Graph.playground/timeline.xctimeline index 6afe06b6f..59717856a 100644 --- a/Graph/Graph.playground/timeline.xctimeline +++ b/Graph/Graph.playground/timeline.xctimeline @@ -3,7 +3,7 @@ version = "3.0"> diff --git a/Graph/Graph.xcodeproj/project.pbxproj b/Graph/Graph.xcodeproj/project.pbxproj index 17a5a7d6b..790f6ac18 100644 --- a/Graph/Graph.xcodeproj/project.pbxproj +++ b/Graph/Graph.xcodeproj/project.pbxproj @@ -158,7 +158,7 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0730; - LastUpgradeCheck = 0900; + LastUpgradeCheck = 1000; ORGANIZATIONNAME = "Swift Algorithm Club"; TargetAttributes = { 49BFA2FC1CDF886B00522D66 = { @@ -251,12 +251,14 @@ 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_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; @@ -289,7 +291,7 @@ ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -308,12 +310,14 @@ 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_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; @@ -339,7 +343,7 @@ MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -364,7 +368,7 @@ SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_SWIFT3_OBJC_INFERENCE = Off; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; }; name = Debug; }; @@ -386,7 +390,7 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SKIP_INSTALL = YES; SWIFT_SWIFT3_OBJC_INFERENCE = Off; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; }; name = Release; }; diff --git a/Graph/Graph.xcodeproj/xcshareddata/xcschemes/Graph.xcscheme b/Graph/Graph.xcodeproj/xcshareddata/xcschemes/Graph.xcscheme index a12d85dad..84c688e66 100644 --- a/Graph/Graph.xcodeproj/xcshareddata/xcschemes/Graph.xcscheme +++ b/Graph/Graph.xcodeproj/xcshareddata/xcschemes/Graph.xcscheme @@ -1,6 +1,6 @@ + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Graph/Graph/Edge.swift b/Graph/Graph/Edge.swift index ac5ed6a25..9ddd4e23d 100644 --- a/Graph/Graph/Edge.swift +++ b/Graph/Graph/Edge.swift @@ -29,13 +29,14 @@ extension Edge: CustomStringConvertible { extension Edge: Hashable { - public var hashValue: Int { - var string = "\(from.description)\(to.description)" + public func hash(into hasher: inout Hasher) { + hasher.combine(from) + hasher.combine(to) if weight != nil { - string.append("\(weight!)") + hasher.combine(weight) } - return string.hashValue } + } public func == (lhs: Edge, rhs: Edge) -> Bool { diff --git a/Graph/Graph/Vertex.swift b/Graph/Graph/Vertex.swift index 758a367bb..f74b38396 100644 --- a/Graph/Graph/Vertex.swift +++ b/Graph/Graph/Vertex.swift @@ -24,9 +24,16 @@ extension Vertex: CustomStringConvertible { extension Vertex: Hashable { - public var hashValue: Int { - return "\(data)\(index)".hashValue - } + + + + public func hasher(into hasher: inout Hasher){ + + hasher.combine(data) + hasher.combine(index) + } + + } diff --git a/Graph/GraphTests/GraphTests.swift b/Graph/GraphTests/GraphTests.swift index 92a4ce828..289d9aa64 100644 --- a/Graph/GraphTests/GraphTests.swift +++ b/Graph/GraphTests/GraphTests.swift @@ -10,13 +10,6 @@ import XCTest class GraphTests: XCTestCase { - func testSwift4() { - // last checked with Xcode 9.0b4 - #if swift(>=4.0) - print("Hello, Swift 4!") - #endif - } - func testAdjacencyMatrixGraphDescription() { let graph = AdjacencyMatrixGraph() diff --git a/Hash Set/HashSet.playground/Contents.swift b/Hash Set/HashSet.playground/Contents.swift index 5edb76bb4..551c6dc86 100644 --- a/Hash Set/HashSet.playground/Contents.swift +++ b/Hash Set/HashSet.playground/Contents.swift @@ -1,8 +1,4 @@ //: Playground - noun: a place where people can play -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif var set = HashSet() diff --git a/Hash Set/HashSet.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Hash Set/HashSet.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Hash Set/HashSet.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Hash Table/HashTable.playground/Contents.swift b/Hash Table/HashTable.playground/Contents.swift index 273113440..121fb6032 100644 --- a/Hash Table/HashTable.playground/Contents.swift +++ b/Hash Table/HashTable.playground/Contents.swift @@ -1,8 +1,5 @@ //: Playground - noun: a place where people can play -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif // Playing with hash values "firstName".hashValue diff --git a/Hash Table/HashTable.playground/Sources/HashTable.swift b/Hash Table/HashTable.playground/Sources/HashTable.swift index 2383b8192..f2a1be040 100644 --- a/Hash Table/HashTable.playground/Sources/HashTable.swift +++ b/Hash Table/HashTable.playground/Sources/HashTable.swift @@ -30,7 +30,7 @@ Insert: O(1) O(n) Delete: O(1) O(n) */ -public struct HashTable: CustomStringConvertible { +public struct HashTable { private typealias Element = (key: Key, value: Value) private typealias Bucket = [Element] private var buckets: [Bucket] @@ -41,23 +41,6 @@ public struct HashTable: CustomStringConvertible { /// A Boolean value that indicates whether the hash table is empty. public var isEmpty: Bool { return count == 0 } - /// A string that represents the contents of the hash table. - public var description: String { - let pairs = buckets.flatMap { b in b.map { e in "\(e.key) = \(e.value)" } } - return pairs.joined(separator: ", ") - } - - /// A string that represents the contents of - /// the hash table, suitable for debugging. - public var debugDescription: String { - var str = "" - for (i, bucket) in buckets.enumerated() { - let pairs = bucket.map { e in "\(e.key) = \(e.value)" } - str += "bucket \(i): " + pairs.joined(separator: ", ") + "\n" - } - return str - } - /** Create a hash table with the given capacity. */ @@ -148,6 +131,25 @@ public struct HashTable: CustomStringConvertible { Returns the given key's array index. */ private func index(forKey key: Key) -> Int { - return abs(key.hashValue) % buckets.count + return abs(key.hashValue % buckets.count) + } +} + +extension HashTable: CustomStringConvertible { + /// A string that represents the contents of the hash table. + public var description: String { + let pairs = buckets.flatMap { b in b.map { e in "\(e.key) = \(e.value)" } } + return pairs.joined(separator: ", ") + } + + /// A string that represents the contents of + /// the hash table, suitable for debugging. + public var debugDescription: String { + var str = "" + for (i, bucket) in buckets.enumerated() { + let pairs = bucket.map { e in "\(e.key) = \(e.value)" } + str += "bucket \(i): " + pairs.joined(separator: ", ") + "\n" + } + return str } } diff --git a/Hash Table/HashTable.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Hash Table/HashTable.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Hash Table/HashTable.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Hash Table/README.markdown b/Hash Table/README.markdown index 9c4065a51..fb0bda788 100644 --- a/Hash Table/README.markdown +++ b/Hash Table/README.markdown @@ -141,7 +141,7 @@ The hash table does not do anything yet, so let's add the remaining functionalit ```swift private func index(forKey key: Key) -> Int { - return abs(key.hashValue) % buckets.count + return abs(key.hashValue % buckets.count) } ``` diff --git a/Hashed Heap/HashedHeap.swift b/Hashed Heap/HashedHeap.swift index be7d9ab78..106cdacb0 100644 --- a/Hashed Heap/HashedHeap.swift +++ b/Hashed Heap/HashedHeap.swift @@ -57,7 +57,12 @@ public struct HashedHeap { public var count: Int { return elements.count } - + + /// Accesses an element by its index. + public subscript(index: Int) -> T { + return elements[index] + } + /// Returns the index of the given element. /// /// This is the operation that a hashed heap optimizes in compassion with a normal heap. In a normal heap this @@ -141,6 +146,12 @@ public struct HashedHeap { } return removeLast() } + + /// Removes all elements from the heap. + public mutating func removeAll() { + elements.removeAll() + indices.removeAll() + } /// Removes the last element from the heap. /// diff --git a/HaversineDistance/HaversineDistance.playground/Contents.swift b/HaversineDistance/HaversineDistance.playground/Contents.swift index 5b3f591d6..ed89b4462 100644 --- a/HaversineDistance/HaversineDistance.playground/Contents.swift +++ b/HaversineDistance/HaversineDistance.playground/Contents.swift @@ -1,10 +1,6 @@ import UIKit -// last checked with Xcode 9.04 -#if swift(>=4) -print("Hello, Swift 4!") -#endif -func haversineDinstance(la1: Double, lo1: Double, la2: Double, lo2: Double, radius: Double = 6367444.7) -> Double { +func haversineDistance(la1: Double, lo1: Double, la2: Double, lo2: Double, radius: Double = 6367444.7) -> Double { let haversin = { (angle: Double) -> Double in return (1 - cos(angle))/2 @@ -16,7 +12,7 @@ func haversineDinstance(la1: Double, lo1: Double, la2: Double, lo2: Double, radi // Converts from degrees to radians let dToR = { (angle: Double) -> Double in - return (angle / 360) * 2 * M_PI + return (angle / 360) * 2 * .pi } let lat1 = dToR(la1) @@ -31,4 +27,4 @@ let amsterdam = (52.3702, 4.8952) let newYork = (40.7128, -74.0059) // Google says it's 5857 km so our result is only off by 2km which could be due to all kinds of things, not sure how google calculates the distance or which latitude and longitude google uses to calculate the distance. -haversineDinstance(la1: amsterdam.0, lo1: amsterdam.1, la2: newYork.0, lo2: newYork.1) +haversineDistance(la1: amsterdam.0, lo1: amsterdam.1, la2: newYork.0, lo2: newYork.1) diff --git a/Heap Sort/HeapSort.swift b/Heap Sort/HeapSort.swift index 6b5e91dc5..0354817a6 100644 --- a/Heap Sort/HeapSort.swift +++ b/Heap Sort/HeapSort.swift @@ -1,10 +1,10 @@ extension Heap { public mutating func sort() -> [T] { - for i in stride(from: (elements.count - 1), through: 1, by: -1) { - elements.swapAt(0, i) - shiftDown(0, heapSize: i) + for i in stride(from: (nodes.count - 1), through: 1, by: -1) { + nodes.swapAt(0, i) + shiftDown(from: 0, until: i) } - return elements + return nodes } } diff --git a/Heap Sort/README.markdown b/Heap Sort/README.markdown index a45dad6f9..5bec58626 100644 --- a/Heap Sort/README.markdown +++ b/Heap Sort/README.markdown @@ -42,7 +42,7 @@ As you can see, the largest items are making their way to the back. We repeat th > **Note:** This process is very similar to [selection sort](../Selection%20Sort/), which repeatedly looks for the minimum item in the remainder of the array. Extracting the minimum or maximum value is what heaps are good at. -Performance of heap sort is **O(n lg n)** in best, worst, and average case. Because we modify the array directly, heap sort can be performed in-place. But it is not a stable sort: the relative order of identical elements is not preserved. +Performance of heap sort is **O(n log n)** in best, worst, and average case. Because we modify the array directly, heap sort can be performed in-place. But it is not a stable sort: the relative order of identical elements is not preserved. Here's how you can implement heap sort in Swift: diff --git a/Heap Sort/Tests/HeapSortTests.swift b/Heap Sort/Tests/HeapSortTests.swift index b36f605e3..dae1f8a68 100644 --- a/Heap Sort/Tests/HeapSortTests.swift +++ b/Heap Sort/Tests/HeapSortTests.swift @@ -1,12 +1,6 @@ import XCTest class HeapSortTests: XCTestCase { - func testSwift4() { - // last checked with Xcode 9.0b4 - #if swift(>=4.0) - print("Hello, Swift 4!") - #endif - } func testSort() { var h1 = Heap(array: [5, 13, 2, 25, 7, 17, 20, 8, 4], sort: >) diff --git a/Heap Sort/Tests/Tests.xcodeproj/project.pbxproj b/Heap Sort/Tests/Tests.xcodeproj/project.pbxproj index ff1f4937f..62e6d4e3f 100644 --- a/Heap Sort/Tests/Tests.xcodeproj/project.pbxproj +++ b/Heap Sort/Tests/Tests.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 51; objects = { /* Begin PBXBuildFile section */ @@ -86,17 +86,17 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0720; - LastUpgradeCheck = 0900; + LastUpgradeCheck = 1010; ORGANIZATIONNAME = "Swift Algorithm Club"; TargetAttributes = { 7B2BBC7F1C779D720067B71D = { CreatedOnToolsVersion = 7.2; - LastSwiftMigration = 0820; + LastSwiftMigration = 1010; }; }; }; buildConfigurationList = 7B2BBC6C1C779D710067B71D /* Build configuration list for PBXProject "Tests" */; - compatibilityVersion = "Xcode 3.2"; + compatibilityVersion = "Xcode 10.0"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( @@ -149,12 +149,14 @@ 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_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; @@ -201,12 +203,14 @@ 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_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; @@ -230,7 +234,8 @@ MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; }; name = Release; }; @@ -239,10 +244,14 @@ buildSettings = { COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = swift.algorithm.club.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; }; name = Debug; }; @@ -251,10 +260,14 @@ buildSettings = { COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = swift.algorithm.club.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; }; name = Release; }; diff --git a/Heap Sort/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Heap Sort/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Heap Sort/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Heap Sort/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme b/Heap Sort/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme index 538a6f4fa..1608804f9 100644 --- a/Heap Sort/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme +++ b/Heap Sort/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme @@ -1,6 +1,6 @@ { + /** The array that stores the heap's nodes. */ - var elements = [T]() + var nodes = [T]() - /** Determines whether this is a max-heap (>) or min-heap (<). */ - fileprivate var isOrderedBefore: (T, T) -> Bool + /** + * Determines how to compare two nodes in the heap. + * Use '>' for a max-heap or '<' for a min-heap, + * or provide a comparing method if the heap is made + * of custom elements, for example tuples. + */ + private var orderCriteria: (T, T) -> Bool /** * Creates an empty heap. * The sort function determines whether this is a min-heap or max-heap. - * For integers, > makes a max-heap, < makes a min-heap. + * For comparable data types, > makes a max-heap, < makes a min-heap. */ public init(sort: @escaping (T, T) -> Bool) { - self.isOrderedBefore = sort + self.orderCriteria = sort } /** * Creates a heap from an array. The order of the array does not matter; * the elements are inserted into the heap in the order determined by the - * sort function. + * sort function. For comparable data types, '>' makes a max-heap, + * '<' makes a min-heap. */ public init(array: [T], sort: @escaping (T, T) -> Bool) { - self.isOrderedBefore = sort - buildHeap(fromArray: array) - } - - /* - // This version has O(n log n) performance. - private mutating func buildHeap(array: [T]) { - elements.reserveCapacity(array.count) - for value in array { - insert(value) - } - } - */ + self.orderCriteria = sort + configureHeap(from: array) + } /** - * Converts an array to a max-heap or min-heap in a bottom-up manner. + * Configures the max-heap or min-heap from an array, in a bottom-up manner. * Performance: This runs pretty much in O(n). */ - fileprivate mutating func buildHeap(fromArray array: [T]) { - elements = array - for i in stride(from: (elements.count/2 - 1), through: 0, by: -1) { - shiftDown(i, heapSize: elements.count) + private mutating func configureHeap(from array: [T]) { + nodes = array + for i in stride(from: (nodes.count/2-1), through: 0, by: -1) { + shiftDown(i) } } public var isEmpty: Bool { - return elements.isEmpty + return nodes.isEmpty } public var count: Int { - return elements.count + return nodes.count } /** * Returns the index of the parent of the element at index i. * The element at index 0 is the root of the tree and has no parent. */ - @inline(__always) func parentIndex(ofIndex i: Int) -> Int { + @inline(__always) internal func parentIndex(ofIndex i: Int) -> Int { return (i - 1) / 2 } @@ -71,7 +68,7 @@ public struct Heap { * Note that this index can be greater than the heap size, in which case * there is no left child. */ - @inline(__always) func leftChildIndex(ofIndex i: Int) -> Int { + @inline(__always) internal func leftChildIndex(ofIndex i: Int) -> Int { return 2*i + 1 } @@ -80,7 +77,7 @@ public struct Heap { * Note that this index can be greater than the heap size, in which case * there is no right child. */ - @inline(__always) func rightChildIndex(ofIndex i: Int) -> Int { + @inline(__always) internal func rightChildIndex(ofIndex i: Int) -> Int { return 2*i + 2 } @@ -89,7 +86,7 @@ public struct Heap { * value (for a min-heap). */ public func peek() -> T? { - return elements.first + return nodes.first } /** @@ -97,10 +94,14 @@ public struct Heap { * or min-heap property still holds. Performance: O(log n). */ public mutating func insert(_ value: T) { - elements.append(value) - shiftUp(elements.count - 1) + nodes.append(value) + shiftUp(nodes.count - 1) } + /** + * Adds a sequence of values to the heap. This reorders the heap so that + * the max-heap or min-heap property still holds. Performance: O(log n). + */ public mutating func insert(_ sequence: S) where S.Iterator.Element == T { for value in sequence { insert(value) @@ -108,15 +109,14 @@ public struct Heap { } /** - * Allows you to change an element. In a max-heap, the new element should be - * larger than the old one; in a min-heap it should be smaller. + * Allows you to change an element. This reorders the heap so that + * the max-heap or min-heap property still holds. */ public mutating func replace(index i: Int, value: T) { - guard i < elements.count else { return } + guard i < nodes.count else { return } - assert(isOrderedBefore(value, elements[i])) - elements[i] = value - shiftUp(i) + remove(at: i) + insert(value) } /** @@ -124,104 +124,100 @@ public struct Heap { * value; for a min-heap it is the minimum value. Performance: O(log n). */ @discardableResult public mutating func remove() -> T? { - if elements.isEmpty { - return nil - } else if elements.count == 1 { - return elements.removeLast() + guard !nodes.isEmpty else { return nil } + + if nodes.count == 1 { + return nodes.removeLast() } else { // Use the last node to replace the first one, then fix the heap by // shifting this new first node into its proper position. - let value = elements[0] - elements[0] = elements.removeLast() - shiftDown() + let value = nodes[0] + nodes[0] = nodes.removeLast() + shiftDown(0) return value } } /** - * Removes an arbitrary node from the heap. Performance: O(log n). You need - * to know the node's index, which may actually take O(n) steps to find. + * Removes an arbitrary node from the heap. Performance: O(log n). + * Note that you need to know the node's index. */ - public mutating func removeAt(_ index: Int) -> T? { - guard index < elements.count else { return nil } + @discardableResult public mutating func remove(at index: Int) -> T? { + guard index < nodes.count else { return nil } - let size = elements.count - 1 + let size = nodes.count - 1 if index != size { - elements.swapAt(index, size) - shiftDown(index, heapSize: size) + nodes.swapAt(index, size) + shiftDown(from: index, until: size) shiftUp(index) } - return elements.removeLast() + return nodes.removeLast() } /** * Takes a child node and looks at its parents; if a parent is not larger * (max-heap) or not smaller (min-heap) than the child, we exchange them. */ - mutating func shiftUp(_ index: Int) { + internal mutating func shiftUp(_ index: Int) { var childIndex = index - let child = elements[childIndex] + let child = nodes[childIndex] var parentIndex = self.parentIndex(ofIndex: childIndex) - while childIndex > 0 && isOrderedBefore(child, elements[parentIndex]) { - elements[childIndex] = elements[parentIndex] + while childIndex > 0 && orderCriteria(child, nodes[parentIndex]) { + nodes[childIndex] = nodes[parentIndex] childIndex = parentIndex parentIndex = self.parentIndex(ofIndex: childIndex) } - elements[childIndex] = child - } - - mutating func shiftDown() { - shiftDown(0, heapSize: elements.count) + nodes[childIndex] = child } /** * Looks at a parent node and makes sure it is still larger (max-heap) or * smaller (min-heap) than its childeren. */ - mutating func shiftDown(_ index: Int, heapSize: Int) { - var parentIndex = index + internal mutating func shiftDown(from index: Int, until endIndex: Int) { + let leftChildIndex = self.leftChildIndex(ofIndex: index) + let rightChildIndex = leftChildIndex + 1 - while true { - let leftChildIndex = self.leftChildIndex(ofIndex: parentIndex) - let rightChildIndex = leftChildIndex + 1 - - // Figure out which comes first if we order them by the sort function: - // the parent, the left child, or the right child. If the parent comes - // first, we're done. If not, that element is out-of-place and we make - // it "float down" the tree until the heap property is restored. - var first = parentIndex - if leftChildIndex < heapSize && isOrderedBefore(elements[leftChildIndex], elements[first]) { - first = leftChildIndex - } - if rightChildIndex < heapSize && isOrderedBefore(elements[rightChildIndex], elements[first]) { - first = rightChildIndex - } - if first == parentIndex { return } - - elements.swapAt(parentIndex, first) - parentIndex = first + // Figure out which comes first if we order them by the sort function: + // the parent, the left child, or the right child. If the parent comes + // first, we're done. If not, that element is out-of-place and we make + // it "float down" the tree until the heap property is restored. + var first = index + if leftChildIndex < endIndex && orderCriteria(nodes[leftChildIndex], nodes[first]) { + first = leftChildIndex } + if rightChildIndex < endIndex && orderCriteria(nodes[rightChildIndex], nodes[first]) { + first = rightChildIndex + } + if first == index { return } + + nodes.swapAt(index, first) + shiftDown(from: first, until: endIndex) } + + internal mutating func shiftDown(_ index: Int) { + shiftDown(from: index, until: nodes.count) + } + } // MARK: - Searching extension Heap where T: Equatable { - /** - * Searches the heap for the given element. Performance: O(n). - */ - public func index(of element: T) -> Int? { - return index(of: element, 0) + + /** Get the index of a node in the heap. Performance: O(n). */ + public func index(of node: T) -> Int? { + return nodes.index(where: { $0 == node }) } - fileprivate func index(of element: T, _ i: Int) -> Int? { - if i >= count { return nil } - if isOrderedBefore(element, elements[i]) { return nil } - if element == elements[i] { return i } - if let j = index(of: element, self.leftChildIndex(ofIndex: i)) { return j } - if let j = index(of: element, self.rightChildIndex(ofIndex: i)) { return j } + /** Removes the first occurrence of a node from the heap. Performance: O(n). */ + @discardableResult public mutating func remove(node: T) -> T? { + if let index = index(of: node) { + return remove(at: index) + } return nil } + } diff --git a/Heap/README.markdown b/Heap/README.markdown old mode 100644 new mode 100755 index 4344d9e7c..fee881129 --- a/Heap/README.markdown +++ b/Heap/README.markdown @@ -29,7 +29,7 @@ As a result of this heap property, a max-heap always stores its largest item at ## How does a heap compare to regular trees? -A heap is not a replacement for a binary search tree, and there are similarities and differnces between them. Here are some main differences: +A heap is not a replacement for a binary search tree, and there are similarities and differences between them. Here are some main differences: **Order of the nodes.** In a [binary search tree (BST)](../Binary%20Search%20Tree/), the left child must be smaller than its parent, and the right child must be greater. This is not true for a heap. In a max-heap both children must be smaller than the parent, while in a min-heap they both must be greater. @@ -38,7 +38,7 @@ A heap is not a replacement for a binary search tree, and there are similarities **Balancing.** A binary search tree must be "balanced" so that most operations have **O(log n)** performance. You can either insert and delete your data in a random order or use something like an [AVL tree](../AVL%20Tree/) or [red-black tree](../Red-Black%20Tree/), but with heaps we don't actually need the entire tree to be sorted. We just want the heap property to be fulfilled, so balancing isn't an issue. Because of the way the heap is structured, heaps can guarantee **O(log n)** performance. -**Searching.** Whereas searching is fast in a binary tree, it is slow in a heap. Searching isn't a top priority in a heap since the purpose of a heap is to put the largest (or smallest) node at the front and to allow relatively fast inserts and deletes. +**Searching.** Whereas searching is fast in a binary tree, it is slow in a heap. Searching isn't a top priority in a heap since the purpose of a heap is to put the largest (or smallest) node at the front and to allow relatively fast inserts and deletes. ## The tree inside an array @@ -82,7 +82,7 @@ array[parent(i)] >= array[i] Verify that this heap property holds for the array from the example heap. -As you can see, these equations allow us to find the parent or child index for any node without the need for pointers. It is complicated than just dereferencing a pointer, but that is the tradeoff: we save memory space but pay with extra computations. Fortunately, the computations are fast and only take **O(1)** time. +As you can see, these equations allow us to find the parent or child index for any node without the need for pointers. It is more complicated than just dereferencing a pointer, but that is the tradeoff: we save memory space but pay with extra computations. Fortunately, the computations are fast and only take **O(1)** time. It is important to understand this relationship between array index and position in the tree. Here is a larger heap which has 15 nodes divided over four levels: @@ -148,7 +148,7 @@ There are two primitive operations necessary to make sure the heap is a valid ma Shifting up or down is a recursive procedure that takes **O(log n)** time. -Here are other operations that are built on primitive operations: +Here are other operations that are built on primitive operations: - `insert(value)`: Adds the new element to the end of the heap and then uses `shiftUp()` to fix the heap. @@ -164,7 +164,7 @@ All of the above take time **O(log n)** because shifting up or down is expensive - `buildHeap(array)`: Converts an (unsorted) array into a heap by repeatedly calling `insert()`. If you are smart about this, it can be done in **O(n)** time. -- [Heap sort](../Heap%20Sort/). Since the heap is an array, we can use its unique properties to sort the array from low to high. Time: **O(n lg n).** +- [Heap sort](../Heap%20Sort/). Since the heap is an array, we can use its unique properties to sort the array from low to high. Time: **O(n log n).** The heap also has a `peek()` function that returns the maximum (max-heap) or minimum (min-heap) element, without removing it from the heap. Time: **O(1)**. @@ -190,7 +190,7 @@ The `(16)` was added to the first available space on the last row. Unfortunately, the heap property is no longer satisfied because `(2)` is above `(16)`, and we want higher numbers above lower numbers. (This is a max-heap.) -To restore the heap property, we swap `(16)` and `(2)`. +To restore the heap property, we swap `(16)` and `(2)`. ![The heap before insertion](Images/Insert2.png) @@ -214,7 +214,7 @@ What happens to the empty spot at the top? ![The root is gone](Images/Remove1.png) -When inserting, we put the new value at the end of the array. Here, we do the opposite: we take the last object we have, stick it up on top of the tree, and restore the heap property. +When inserting, we put the new value at the end of the array. Here, we do the opposite: we take the last object we have, stick it up on top of the tree, and restore the heap property. ![The last node goes to the root](Images/Remove2.png) @@ -226,7 +226,7 @@ Keep shifting down until the node does not have any children or it is larger tha ![The last node goes to the root](Images/Remove4.png) -The time required for shifting all the way down is proportional to the height of the tree which takes **O(log n)** time. +The time required for shifting all the way down is proportional to the height of the tree which takes **O(log n)** time. > **Note:** `shiftUp()` and `shiftDown()` can only fix one out-of-place element at a time. If there are multiple elements in the wrong place, you need to call these functions once for each of those elements. @@ -279,7 +279,7 @@ In code: ```swift private mutating func buildHeap(fromArray array: [T]) { elements = array - for i in (elements.count/2 - 1).stride(through: 0, by: -1) { + for i in stride(from: (nodes.count/2-1), through: 0, by: -1) { shiftDown(index: i, heapSize: elements.count) } } diff --git a/Heap/Tests/HeapTests.swift b/Heap/Tests/HeapTests.swift old mode 100644 new mode 100755 index 66be0931c..ecf143778 --- a/Heap/Tests/HeapTests.swift +++ b/Heap/Tests/HeapTests.swift @@ -7,21 +7,14 @@ import XCTest class HeapTests: XCTestCase { - func testSwift4() { - // last checked with Xcode 9.0b4 - #if swift(>=4.0) - print("Hello, Swift 4!") - #endif - } - fileprivate func verifyMaxHeap(_ h: Heap) -> Bool { for i in 0.. 0 && h.elements[parent] < h.elements[i] { return false } + if left < h.count && h.nodes[i] < h.nodes[left] { return false } + if right < h.count && h.nodes[i] < h.nodes[right] { return false } + if i > 0 && h.nodes[parent] < h.nodes[i] { return false } } return true } @@ -31,9 +24,9 @@ class HeapTests: XCTestCase { let left = h.leftChildIndex(ofIndex: i) let right = h.rightChildIndex(ofIndex: i) let parent = h.parentIndex(ofIndex: i) - if left < h.count && h.elements[i] > h.elements[left] { return false } - if right < h.count && h.elements[i] > h.elements[right] { return false } - if i > 0 && h.elements[parent] > h.elements[i] { return false } + if left < h.count && h.nodes[i] > h.nodes[left] { return false } + if right < h.count && h.nodes[i] > h.nodes[right] { return false } + if i > 0 && h.nodes[parent] > h.nodes[i] { return false } } return true } @@ -90,7 +83,7 @@ class HeapTests: XCTestCase { let h1 = Heap(array: [1, 2, 3, 4, 5, 6, 7], sort: >) XCTAssertTrue(verifyMaxHeap(h1)) XCTAssertFalse(verifyMinHeap(h1)) - XCTAssertEqual(h1.elements, [7, 5, 6, 4, 2, 1, 3]) + XCTAssertEqual(h1.nodes, [7, 5, 6, 4, 2, 1, 3]) XCTAssertFalse(h1.isEmpty) XCTAssertEqual(h1.count, 7) XCTAssertEqual(h1.peek()!, 7) @@ -98,7 +91,7 @@ class HeapTests: XCTestCase { let h2 = Heap(array: [7, 6, 5, 4, 3, 2, 1], sort: >) XCTAssertTrue(verifyMaxHeap(h2)) XCTAssertFalse(verifyMinHeap(h2)) - XCTAssertEqual(h2.elements, [7, 6, 5, 4, 3, 2, 1]) + XCTAssertEqual(h2.nodes, [7, 6, 5, 4, 3, 2, 1]) XCTAssertFalse(h2.isEmpty) XCTAssertEqual(h2.count, 7) XCTAssertEqual(h2.peek()!, 7) @@ -106,7 +99,7 @@ class HeapTests: XCTestCase { let h3 = Heap(array: [4, 1, 3, 2, 16, 9, 10, 14, 8, 7], sort: >) XCTAssertTrue(verifyMaxHeap(h3)) XCTAssertFalse(verifyMinHeap(h3)) - XCTAssertEqual(h3.elements, [16, 14, 10, 8, 7, 9, 3, 2, 4, 1]) + XCTAssertEqual(h3.nodes, [16, 14, 10, 8, 7, 9, 3, 2, 4, 1]) XCTAssertFalse(h3.isEmpty) XCTAssertEqual(h3.count, 10) XCTAssertEqual(h3.peek()!, 16) @@ -114,7 +107,7 @@ class HeapTests: XCTestCase { let h4 = Heap(array: [27, 17, 3, 16, 13, 10, 1, 5, 7, 12, 4, 8, 9, 0], sort: >) XCTAssertTrue(verifyMaxHeap(h4)) XCTAssertFalse(verifyMinHeap(h4)) - XCTAssertEqual(h4.elements, [27, 17, 10, 16, 13, 9, 1, 5, 7, 12, 4, 8, 3, 0]) + XCTAssertEqual(h4.nodes, [27, 17, 10, 16, 13, 9, 1, 5, 7, 12, 4, 8, 3, 0]) XCTAssertFalse(h4.isEmpty) XCTAssertEqual(h4.count, 14) XCTAssertEqual(h4.peek()!, 27) @@ -124,7 +117,7 @@ class HeapTests: XCTestCase { let h1 = Heap(array: [1, 2, 3, 4, 5, 6, 7], sort: <) XCTAssertTrue(verifyMinHeap(h1)) XCTAssertFalse(verifyMaxHeap(h1)) - XCTAssertEqual(h1.elements, [1, 2, 3, 4, 5, 6, 7]) + XCTAssertEqual(h1.nodes, [1, 2, 3, 4, 5, 6, 7]) XCTAssertFalse(h1.isEmpty) XCTAssertEqual(h1.count, 7) XCTAssertEqual(h1.peek()!, 1) @@ -132,7 +125,7 @@ class HeapTests: XCTestCase { let h2 = Heap(array: [7, 6, 5, 4, 3, 2, 1], sort: <) XCTAssertTrue(verifyMinHeap(h2)) XCTAssertFalse(verifyMaxHeap(h2)) - XCTAssertEqual(h2.elements, [1, 3, 2, 4, 6, 7, 5]) + XCTAssertEqual(h2.nodes, [1, 3, 2, 4, 6, 7, 5]) XCTAssertFalse(h2.isEmpty) XCTAssertEqual(h2.count, 7) XCTAssertEqual(h2.peek()!, 1) @@ -140,7 +133,7 @@ class HeapTests: XCTestCase { let h3 = Heap(array: [4, 1, 3, 2, 16, 9, 10, 14, 8, 7], sort: <) XCTAssertTrue(verifyMinHeap(h3)) XCTAssertFalse(verifyMaxHeap(h3)) - XCTAssertEqual(h3.elements, [1, 2, 3, 4, 7, 9, 10, 14, 8, 16]) + XCTAssertEqual(h3.nodes, [1, 2, 3, 4, 7, 9, 10, 14, 8, 16]) XCTAssertFalse(h3.isEmpty) XCTAssertEqual(h3.count, 10) XCTAssertEqual(h3.peek()!, 1) @@ -148,24 +141,24 @@ class HeapTests: XCTestCase { let h4 = Heap(array: [27, 17, 3, 16, 13, 10, 1, 5, 7, 12, 4, 8, 9, 0], sort: <) XCTAssertTrue(verifyMinHeap(h4)) XCTAssertFalse(verifyMaxHeap(h4)) - XCTAssertEqual(h4.elements, [0, 4, 1, 5, 12, 8, 3, 16, 7, 17, 13, 10, 9, 27]) + XCTAssertEqual(h4.nodes, [0, 4, 1, 5, 12, 8, 3, 16, 7, 17, 13, 10, 9, 27]) XCTAssertFalse(h4.isEmpty) XCTAssertEqual(h4.count, 14) XCTAssertEqual(h4.peek()!, 0) } - func testCreateMaxHeapEqualElements() { + func testCreateMaxHeapEqualnodes() { let heap = Heap(array: [1, 1, 1, 1, 1], sort: >) XCTAssertTrue(verifyMaxHeap(heap)) XCTAssertTrue(verifyMinHeap(heap)) - XCTAssertEqual(heap.elements, [1, 1, 1, 1, 1]) + XCTAssertEqual(heap.nodes, [1, 1, 1, 1, 1]) } - func testCreateMinHeapEqualElements() { + func testCreateMinHeapEqualnodes() { let heap = Heap(array: [1, 1, 1, 1, 1], sort: <) XCTAssertTrue(verifyMinHeap(heap)) XCTAssertTrue(verifyMaxHeap(heap)) - XCTAssertEqual(heap.elements, [1, 1, 1, 1, 1]) + XCTAssertEqual(heap.nodes, [1, 1, 1, 1, 1]) } fileprivate func randomArray(_ n: Int) -> [Int] { @@ -183,7 +176,7 @@ class HeapTests: XCTestCase { XCTAssertTrue(verifyMaxHeap(h)) XCTAssertFalse(h.isEmpty) XCTAssertEqual(h.count, n) - XCTAssertTrue(isPermutation(a, h.elements)) + XCTAssertTrue(isPermutation(a, h.nodes)) } } @@ -194,58 +187,58 @@ class HeapTests: XCTestCase { XCTAssertTrue(verifyMinHeap(h)) XCTAssertFalse(h.isEmpty) XCTAssertEqual(h.count, n) - XCTAssertTrue(isPermutation(a, h.elements)) + XCTAssertTrue(isPermutation(a, h.nodes)) } } func testRemoving() { var h = Heap(array: [100, 50, 70, 10, 20, 60, 65], sort: >) XCTAssertTrue(verifyMaxHeap(h)) - XCTAssertEqual(h.elements, [100, 50, 70, 10, 20, 60, 65]) + XCTAssertEqual(h.nodes, [100, 50, 70, 10, 20, 60, 65]) //test index out of bounds - let v = h.removeAt(10) + let v = h.remove(at: 10) XCTAssertEqual(v, nil) XCTAssertTrue(verifyMaxHeap(h)) - XCTAssertEqual(h.elements, [100, 50, 70, 10, 20, 60, 65]) + XCTAssertEqual(h.nodes, [100, 50, 70, 10, 20, 60, 65]) - let v1 = h.removeAt(5) + let v1 = h.remove(at: 5) XCTAssertEqual(v1, 60) XCTAssertTrue(verifyMaxHeap(h)) - XCTAssertEqual(h.elements, [100, 50, 70, 10, 20, 65]) + XCTAssertEqual(h.nodes, [100, 50, 70, 10, 20, 65]) - let v2 = h.removeAt(4) + let v2 = h.remove(at: 4) XCTAssertEqual(v2, 20) XCTAssertTrue(verifyMaxHeap(h)) - XCTAssertEqual(h.elements, [100, 65, 70, 10, 50]) + XCTAssertEqual(h.nodes, [100, 65, 70, 10, 50]) - let v3 = h.removeAt(4) + let v3 = h.remove(at: 4) XCTAssertEqual(v3, 50) XCTAssertTrue(verifyMaxHeap(h)) - XCTAssertEqual(h.elements, [100, 65, 70, 10]) + XCTAssertEqual(h.nodes, [100, 65, 70, 10]) - let v4 = h.removeAt(0) + let v4 = h.remove(at: 0) XCTAssertEqual(v4, 100) XCTAssertTrue(verifyMaxHeap(h)) - XCTAssertEqual(h.elements, [70, 65, 10]) + XCTAssertEqual(h.nodes, [70, 65, 10]) XCTAssertEqual(h.peek()!, 70) let v5 = h.remove() XCTAssertEqual(v5, 70) XCTAssertTrue(verifyMaxHeap(h)) - XCTAssertEqual(h.elements, [65, 10]) + XCTAssertEqual(h.nodes, [65, 10]) XCTAssertEqual(h.peek()!, 65) let v6 = h.remove() XCTAssertEqual(v6, 65) XCTAssertTrue(verifyMaxHeap(h)) - XCTAssertEqual(h.elements, [10]) + XCTAssertEqual(h.nodes, [10]) XCTAssertEqual(h.peek()!, 10) let v7 = h.remove() XCTAssertEqual(v7, 10) XCTAssertTrue(verifyMaxHeap(h)) - XCTAssertEqual(h.elements, []) + XCTAssertEqual(h.nodes, []) XCTAssertNil(h.peek()) } @@ -259,12 +252,12 @@ class HeapTests: XCTestCase { func testRemoveRoot() { var h = Heap(array: [15, 13, 9, 5, 12, 8, 7, 4, 0, 6, 2, 1], sort: >) XCTAssertTrue(verifyMaxHeap(h)) - XCTAssertEqual(h.elements, [15, 13, 9, 5, 12, 8, 7, 4, 0, 6, 2, 1]) + XCTAssertEqual(h.nodes, [15, 13, 9, 5, 12, 8, 7, 4, 0, 6, 2, 1]) XCTAssertEqual(h.peek()!, 15) let v = h.remove() XCTAssertEqual(v, 15) XCTAssertTrue(verifyMaxHeap(h)) - XCTAssertEqual(h.elements, [13, 12, 9, 5, 6, 8, 7, 4, 0, 1, 2]) + XCTAssertEqual(h.nodes, [13, 12, 9, 5, 6, 8, 7, 4, 0, 1, 2]) } func testRemoveRandomItems() { @@ -272,19 +265,19 @@ class HeapTests: XCTestCase { var a = randomArray(n) var h = Heap(array: a, sort: >) XCTAssertTrue(verifyMaxHeap(h)) - XCTAssertTrue(isPermutation(a, h.elements)) + XCTAssertTrue(isPermutation(a, h.nodes)) let m = (n + 1)/2 for k in 1...m { let i = Int(arc4random_uniform(UInt32(n - k + 1))) - let v = h.removeAt(i)! + let v = h.remove(at: i)! let j = a.index(of: v)! a.remove(at: j) XCTAssertTrue(verifyMaxHeap(h)) XCTAssertEqual(h.count, a.count) XCTAssertEqual(h.count, n - k) - XCTAssertTrue(isPermutation(a, h.elements)) + XCTAssertTrue(isPermutation(a, h.nodes)) } } } @@ -292,17 +285,17 @@ class HeapTests: XCTestCase { func testInsert() { var h = Heap(array: [15, 13, 9, 5, 12, 8, 7, 4, 0, 6, 2, 1], sort: >) XCTAssertTrue(verifyMaxHeap(h)) - XCTAssertEqual(h.elements, [15, 13, 9, 5, 12, 8, 7, 4, 0, 6, 2, 1]) + XCTAssertEqual(h.nodes, [15, 13, 9, 5, 12, 8, 7, 4, 0, 6, 2, 1]) h.insert(10) XCTAssertTrue(verifyMaxHeap(h)) - XCTAssertEqual(h.elements, [15, 13, 10, 5, 12, 9, 7, 4, 0, 6, 2, 1, 8]) + XCTAssertEqual(h.nodes, [15, 13, 10, 5, 12, 9, 7, 4, 0, 6, 2, 1, 8]) } func testInsertArrayAndRemove() { var heap = Heap(sort: >) heap.insert([1, 3, 2, 7, 5, 9]) - XCTAssertEqual(heap.elements, [9, 5, 7, 1, 3, 2]) + XCTAssertEqual(heap.nodes, [9, 5, 7, 1, 3, 2]) XCTAssertEqual(9, heap.remove()) XCTAssertEqual(7, heap.remove()) @@ -316,15 +309,13 @@ class HeapTests: XCTestCase { func testReplace() { var h = Heap(array: [16, 14, 10, 8, 7, 9, 3, 2, 4, 1], sort: >) XCTAssertTrue(verifyMaxHeap(h)) - XCTAssertEqual(h.elements, [16, 14, 10, 8, 7, 9, 3, 2, 4, 1]) h.replace(index: 5, value: 13) XCTAssertTrue(verifyMaxHeap(h)) - XCTAssertEqual(h.elements, [16, 14, 13, 8, 7, 10, 3, 2, 4, 1]) //test index out of bounds h.replace(index: 20, value: 2) XCTAssertTrue(verifyMaxHeap(h)) - XCTAssertEqual(h.elements, [16, 14, 13, 8, 7, 10, 3, 2, 4, 1]) } + } diff --git a/Heap/Tests/Tests.xcodeproj/project.pbxproj b/Heap/Tests/Tests.xcodeproj/project.pbxproj old mode 100644 new mode 100755 index 617e29f27..0ab48ba3b --- a/Heap/Tests/Tests.xcodeproj/project.pbxproj +++ b/Heap/Tests/Tests.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 46; + objectVersion = 51; objects = { /* Begin PBXBuildFile section */ @@ -83,17 +83,17 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0720; - LastUpgradeCheck = 0800; + LastUpgradeCheck = 1010; ORGANIZATIONNAME = "Swift Algorithm Club"; TargetAttributes = { 7B2BBC7F1C779D720067B71D = { CreatedOnToolsVersion = 7.2; - LastSwiftMigration = 0820; + LastSwiftMigration = 1010; }; }; }; buildConfigurationList = 7B2BBC6C1C779D710067B71D /* Build configuration list for PBXProject "Tests" */; - compatibilityVersion = "Xcode 3.2"; + compatibilityVersion = "Xcode 10.0"; developmentRegion = English; hasScannedForEncodings = 0; knownRegions = ( @@ -141,14 +141,22 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = 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_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_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -187,14 +195,22 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = 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_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_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -214,7 +230,8 @@ MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; }; name = Release; }; @@ -223,10 +240,14 @@ buildSettings = { COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = swift.algorithm.club.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; }; name = Debug; }; @@ -235,10 +256,14 @@ buildSettings = { COMBINE_HIDPI_IMAGES = YES; INFOPLIST_FILE = Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); PRODUCT_BUNDLE_IDENTIFIER = swift.algorithm.club.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; }; name = Release; }; diff --git a/Heap/Tests/Tests.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Heap/Tests/Tests.xcodeproj/project.xcworkspace/contents.xcworkspacedata old mode 100644 new mode 100755 diff --git a/Heap/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Heap/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Heap/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Heap/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme b/Heap/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme old mode 100644 new mode 100755 index 80ed35600..1608804f9 --- a/Heap/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme +++ b/Heap/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme @@ -1,6 +1,6 @@ =4.0) -print("Hello, Swift 4!") -#endif - import Foundation let s1 = "so much words wow many compression" diff --git a/Images/DataStructuresAndAlgorithmsInSwiftBook.png b/Images/DataStructuresAndAlgorithmsInSwiftBook.png new file mode 100644 index 000000000..f8de0ba72 Binary files /dev/null and b/Images/DataStructuresAndAlgorithmsInSwiftBook.png differ diff --git a/Insertion Sort/InsertionSort.playground/Contents.swift b/Insertion Sort/InsertionSort.playground/Contents.swift index 91e18640f..184fc36e4 100644 --- a/Insertion Sort/InsertionSort.playground/Contents.swift +++ b/Insertion Sort/InsertionSort.playground/Contents.swift @@ -1,24 +1,46 @@ //: Playground - noun: a place where people can play -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - +/// Performs the Insertion sort algorithm to a given array +/// +/// - Parameters: +/// - array: the array of elements to be sorted +/// - isOrderedBefore: returns true if the elements provided are in the corect order +/// - Returns: a sorted array containing the same elements func insertionSort(_ array: [T], _ isOrderedBefore: (T, T) -> Bool) -> [T] { - var a = array - for x in 1.. 0 && isOrderedBefore(temp, a[y - 1]) { - a[y] = a[y - 1] - y -= 1 + guard array.count > 1 else { return array } + + var a = array + for x in 1.. 0 && isOrderedBefore(temp, a[y - 1]) { + a[y] = a[y - 1] + y -= 1 + } + a[y] = temp + } + return a +} + +/// Performs the Insertion sort algorithm to a given array +/// +/// - Parameter array: the array to be sorted, conatining elements that conform to the Comparable protocol +/// - Returns: a sorted array containing the same elements +func insertionSort(_ array: [T]) -> [T] { + var a = array + for x in 1.. 0 && temp < a[y - 1] { + a[y] = a[y - 1] + y -= 1 + } + a[y] = temp } - a[y] = temp - } - return a + return a } let list = [ 10, -1, 3, 9, 2, 27, 8, 5, 1, 3, 0, 26 ] -insertionSort(list, <) -insertionSort(list, >) +print(insertionSort(list)) +print(insertionSort(list, <)) +print(insertionSort(list, >)) diff --git a/Insertion Sort/InsertionSort.playground/contents.xcplayground b/Insertion Sort/InsertionSort.playground/contents.xcplayground index 06828af92..9f9eecc96 100644 --- a/Insertion Sort/InsertionSort.playground/contents.xcplayground +++ b/Insertion Sort/InsertionSort.playground/contents.xcplayground @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/Insertion Sort/InsertionSort.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Insertion Sort/InsertionSort.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Insertion Sort/InsertionSort.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Insertion Sort/InsertionSort.swift b/Insertion Sort/InsertionSort.swift index 5d8a9565b..2ad607ac8 100644 --- a/Insertion Sort/InsertionSort.swift +++ b/Insertion Sort/InsertionSort.swift @@ -1,3 +1,9 @@ +/// Performs the Insertion sort algorithm to a given array +/// +/// - Parameters: +/// - array: the array of elements to be sorted +/// - isOrderedBefore: returns true if the elements provided are in the corect order +/// - Returns: a sorted array containing the same elements func insertionSort(_ array: [T], _ isOrderedBefore: (T, T) -> Bool) -> [T] { guard array.count > 1 else { return array } @@ -13,3 +19,23 @@ func insertionSort(_ array: [T], _ isOrderedBefore: (T, T) -> Bool) -> [T] { } return a } + +/// Performs the Insertion sort algorithm to a given array +/// +/// - Parameter array: the array to be sorted, containing elements that conform to the Comparable protocol +/// - Returns: a sorted array containing the same elements +func insertionSort(_ array: [T]) -> [T] { + guard array.count > 1 else { return array } + + var a = array + for x in 1.. 0 && temp < a[y - 1] { + a[y] = a[y - 1] + y -= 1 + } + a[y] = temp + } + return a +} diff --git a/Insertion Sort/Tests/Tests.xcodeproj/project.pbxproj b/Insertion Sort/Tests/Tests.xcodeproj/project.pbxproj index ee3ca1cae..e9ea3bc53 100644 --- a/Insertion Sort/Tests/Tests.xcodeproj/project.pbxproj +++ b/Insertion Sort/Tests/Tests.xcodeproj/project.pbxproj @@ -86,12 +86,12 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0720; - LastUpgradeCheck = 0800; + LastUpgradeCheck = 1000; ORGANIZATIONNAME = "Swift Algorithm Club"; TargetAttributes = { 7B2BBC7F1C779D720067B71D = { CreatedOnToolsVersion = 7.2; - LastSwiftMigration = 0800; + LastSwiftMigration = 1000; }; }; }; @@ -145,14 +145,22 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = 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_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_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -191,14 +199,22 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = 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_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_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -230,7 +246,8 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = swift.algorithm.club.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = On; + SWIFT_VERSION = 4.2; }; name = Debug; }; @@ -242,7 +259,8 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = swift.algorithm.club.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = On; + SWIFT_VERSION = 4.2; }; name = Release; }; diff --git a/Insertion Sort/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Insertion Sort/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Insertion Sort/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Insertion Sort/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme b/Insertion Sort/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme index 14f27f777..afd69e6a7 100644 --- a/Insertion Sort/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme +++ b/Insertion Sort/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme @@ -1,6 +1,6 @@ (_ elements: inout [T], _ index: Int, _ range: Range, by areInIncreasingOrder: (T, T) -> Bool) { + let countToIndex = elements.distance(from: range.lowerBound, to: index) + let countFromIndex = elements.distance(from: index, to: range.upperBound) + + guard countToIndex + 1 < countFromIndex else { return } + + let left = elements.index(index, offsetBy: countToIndex + 1) + var largest = index + if areInIncreasingOrder(elements[largest], elements[left]) { + largest = left + } + + if countToIndex + 2 < countFromIndex { + let right = elements.index(after: left) + if areInIncreasingOrder(elements[largest], elements[right]) { + largest = right + } + } + + if largest != index { + elements.swapAt(index, largest) + shiftDown(&elements, largest, range, by: areInIncreasingOrder) + } + +} + +private func heapify(_ list: inout [T], _ range: Range, by areInIncreasingOrder: (T, T) -> Bool) { + let root = range.lowerBound + var node = list.index(root, offsetBy: list.distance(from: range.lowerBound, to: range.upperBound)/2) + + while node != root { + list.formIndex(before: &node) + shiftDown(&list, node, range, by: areInIncreasingOrder) + } +} + +public func heapsort(for array: inout [T], range: Range, by areInIncreasingOrder: (T, T) -> Bool) { + var hi = range.upperBound + let lo = range.lowerBound + heapify(&array, range, by: areInIncreasingOrder) + array.formIndex(before: &hi) + + while hi != lo { + array.swapAt(lo, hi) + shiftDown(&array, lo, lo..(for array: inout [T], range: Range, by areInIncreasingOrder: (T, T) -> Bool) { + guard !range.isEmpty else { return } + + let start = range.lowerBound + var sortedEnd = start + + array.formIndex(after: &sortedEnd) + while sortedEnd != range.upperBound { + let x = array[sortedEnd] + + var i = sortedEnd + repeat { + let predecessor = array[array.index(before: i)] + + guard areInIncreasingOrder(x, predecessor) else { break } + array[i] = predecessor + array.formIndex(before: &i) + } while i != start + + if i != sortedEnd { + array[i] = x + } + array.formIndex(after: &sortedEnd) + } + +} diff --git a/Introsort/IntroSort.swift b/Introsort/IntroSort.swift new file mode 100644 index 000000000..866b2603f --- /dev/null +++ b/Introsort/IntroSort.swift @@ -0,0 +1,32 @@ +import Foundation + +public func introsort(_ array: inout [T], by areInIncreasingOrder: (T, T) -> Bool) { + //The depth limit is as best practice 2 * log( n ) + let depthLimit = 2 * floor(log2(Double(array.count))) + + introSortImplementation(for: &array, range: 0..(for array: inout [T], range: Range, depthLimit: Int, by areInIncreasingOrder: (T, T) -> Bool) { + if array.distance(from: range.lowerBound, to: range.upperBound) < 20 { + //if the partition count is less than 20 we can sort it using insertion sort. This algorithm in fact performs well on collections + //of this size, plus, at this point is quite probable that the quisksort part of the algorithm produced a partition which is + //nearly sorted. As we knoe insertion sort tends to O( n ) if this is the case. + insertionSort(for: &array, range: range, by: areInIncreasingOrder) + } else if depthLimit == 0 { + //If we reached the depth limit for this recursion branch, it's possible that we are hitting quick sort's worst case. + //Since quicksort degrades to O( n^2 ) in its worst case we stop using quicksort for this recursion branch and we switch to heapsort. + //Our preference remains quicksort, and we hope to be rare to see this condition to be true + heapsort(for: &array, range: range, by: areInIncreasingOrder) + } else { + //By default we use quicksort to sort our collection. The partition index method chose a pivot, and puts all the + //elements less than pivot on the left, and the ones bigger than pivot on the right. At the end of the operation the + //position of the pivot in the array is returned so that we can form the two partitions. + let partIdx = partitionIndex(for: &array, subRange: range, by: areInIncreasingOrder) + + //We can recursively call introsort implementation, decreasing the depthLimit for the left partition and the right partition. + introSortImplementation(for: &array, range: range.lowerBound..(_ array: inout [T], by areInIncreasingOrder: (T, T) -> Bool) { + //The depth limit is as best practice 2 * log( n ) + let depthLimit = 2 * floor(log2(Double(array.count))) + + introSortImplementation(for: &array, range: 0..(for array: inout [T], range: Range, depthLimit: Int, by areInIncreasingOrder: (T, T) -> Bool) { + if array.distance(from: range.lowerBound, to: range.upperBound) < 20 { + //if the partition count is less than 20 we can sort it using insertion sort. This algorithm in fact performs well on collections + //of this size, plus, at this point is quite probable that the quisksort part of the algorithm produced a partition which is + //nearly sorted. As we knoe insertion sort tends to O( n ) if this is the case. + insertionSort(for: &array, range: range, by: areInIncreasingOrder) + } else if depthLimit == 0 { + //If we reached the depth limit for this recursion branch, it's possible that we are hitting quick sort's worst case. + //Since quicksort degrades to O( n^2 ) in its worst case we stop using quicksort for this recursion branch and we switch to heapsort. + //Our preference remains quicksort, and we hope to be rare to see this condition to be true + heapsort(for: &array, range: range, by: areInIncreasingOrder) + } else { + //By default we use quicksort to sort our collection. The partition index method chose a pivot, and puts all the + //elements less than pivot on the left, and the ones bigger than pivot on the right. At the end of the operation the + //position of the pivot in the array is returned so that we can form the two partitions. + let partIdx = partitionIndex(for: &array, subRange: range, by: areInIncreasingOrder) + + //We can recursively call introsort implementation, decreasing the depthLimit for the left partition and the right partition. + introSortImplementation(for: &array, range: range.lowerBound..(_ elements: inout [T], _ index: Int, _ range: Range, by areInIncreasingOrder: (T, T) -> Bool) { + let countToIndex = elements.distance(from: range.lowerBound, to: index) + let countFromIndex = elements.distance(from: index, to: range.upperBound) + + guard countToIndex + 1 < countFromIndex else { return } + + let left = elements.index(index, offsetBy: countToIndex + 1) + var largest = index + if areInIncreasingOrder(elements[largest], elements[left]) { + largest = left + } + + if countToIndex + 2 < countFromIndex { + let right = elements.index(after: left) + if areInIncreasingOrder(elements[largest], elements[right]) { + largest = right + } + } + + if largest != index { + elements.swapAt(index, largest) + shiftDown(&elements, largest, range, by: areInIncreasingOrder) + } + +} + +private func heapify(_ list: inout [T], _ range: Range, by areInIncreasingOrder: (T, T) -> Bool) { + let root = range.lowerBound + var node = list.index(root, offsetBy: list.distance(from: range.lowerBound, to: range.upperBound)/2) + + while node != root { + list.formIndex(before: &node) + shiftDown(&list, node, range, by: areInIncreasingOrder) + } +} + +public func heapsort(for array: inout [T], range: Range, by areInIncreasingOrder: (T, T) -> Bool) { + var hi = range.upperBound + let lo = range.lowerBound + heapify(&array, range, by: areInIncreasingOrder) + array.formIndex(before: &hi) + + while hi != lo { + array.swapAt(lo, hi) + shiftDown(&array, lo, lo..(for array: inout [T], range: Range, by areInIncreasingOrder: (T, T) -> Bool) { + guard !range.isEmpty else { return } + + let start = range.lowerBound + var sortedEnd = start + + array.formIndex(after: &sortedEnd) + while sortedEnd != range.upperBound { + let x = array[sortedEnd] + + var i = sortedEnd + repeat { + let predecessor = array[array.index(before: i)] + + guard areInIncreasingOrder(x, predecessor) else { break } + array[i] = predecessor + array.formIndex(before: &i) + } while i != start + + if i != sortedEnd { + array[i] = x + } + array.formIndex(after: &sortedEnd) + } + +} diff --git a/Introsort/Introsort.playground/Sources/Partition.swift b/Introsort/Introsort.playground/Sources/Partition.swift new file mode 100644 index 000000000..e24636da7 --- /dev/null +++ b/Introsort/Introsort.playground/Sources/Partition.swift @@ -0,0 +1,43 @@ +import Foundation + +public func partitionIndex(for elements: inout [T], subRange range: Range, by areInIncreasingOrder: (T, T) -> Bool) -> Int { + var lo = range.lowerBound + var hi = elements.index(before: range.upperBound) + + // Sort the first, middle, and last elements, then use the middle value + // as the pivot for the partition. + let half = elements.distance(from: lo, to: hi) / 2 + let mid = elements.index(lo, offsetBy: half) + + sort3(in: &elements, a: lo, b: mid, c: hi, by: areInIncreasingOrder) + let pivot = elements[mid] + + while true { + elements.formIndex(after: &lo) + guard findLo(in: elements, pivot: pivot, from: &lo, to: hi, by: areInIncreasingOrder) else { break } + elements.formIndex(before: &hi) + guard findHi(in: elements, pivot: pivot, from: lo, to: &hi, by: areInIncreasingOrder) else { break } + elements.swapAt(lo, hi) + } + + + return lo +} + +private func findLo(in array: [T], pivot: T, from lo: inout Int, to hi: Int, by areInIncreasingOrder: (T, T)->Bool) -> Bool { + while lo != hi { + if !areInIncreasingOrder(array[lo], pivot) { + return true + } + array.formIndex(after: &lo) + } + return false +} + +private func findHi(in array: [T], pivot: T, from lo: Int, to hi: inout Int, by areInIncreasingOrder: (T, T)->Bool) -> Bool { + while hi != lo { + if areInIncreasingOrder(array[hi], pivot) { return true } + array.formIndex(before: &hi) + } + return false +} diff --git a/Introsort/Introsort.playground/Sources/Randomize.swift b/Introsort/Introsort.playground/Sources/Randomize.swift new file mode 100644 index 000000000..6d1d57132 --- /dev/null +++ b/Introsort/Introsort.playground/Sources/Randomize.swift @@ -0,0 +1,9 @@ +import Foundation + +public func randomize(n: Int) -> [Int] { + var unsorted = [Int]() + for _ in 0..(in array: inout [T], a: Int, b: Int, c: Int, by areInIncreasingOrder: (T, T) -> Bool) { + switch (areInIncreasingOrder(array[b], array[a]), + areInIncreasingOrder(array[c], array[b])) { + case (false, false): break + case (true, true): array.swapAt(a, c) + case (true, false): + array.swapAt(a, b) + + if areInIncreasingOrder(array[c], array[b]) { + array.swapAt(b, c) + } + case (false, true): + array.swapAt(b, c) + + if areInIncreasingOrder(array[b], array[a]) { + array.swapAt(a, b) + } + } +} diff --git a/Introsort/Introsort.playground/contents.xcplayground b/Introsort/Introsort.playground/contents.xcplayground new file mode 100644 index 000000000..5da2641c9 --- /dev/null +++ b/Introsort/Introsort.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Introsort/Partition.swift b/Introsort/Partition.swift new file mode 100644 index 000000000..e24636da7 --- /dev/null +++ b/Introsort/Partition.swift @@ -0,0 +1,43 @@ +import Foundation + +public func partitionIndex(for elements: inout [T], subRange range: Range, by areInIncreasingOrder: (T, T) -> Bool) -> Int { + var lo = range.lowerBound + var hi = elements.index(before: range.upperBound) + + // Sort the first, middle, and last elements, then use the middle value + // as the pivot for the partition. + let half = elements.distance(from: lo, to: hi) / 2 + let mid = elements.index(lo, offsetBy: half) + + sort3(in: &elements, a: lo, b: mid, c: hi, by: areInIncreasingOrder) + let pivot = elements[mid] + + while true { + elements.formIndex(after: &lo) + guard findLo(in: elements, pivot: pivot, from: &lo, to: hi, by: areInIncreasingOrder) else { break } + elements.formIndex(before: &hi) + guard findHi(in: elements, pivot: pivot, from: lo, to: &hi, by: areInIncreasingOrder) else { break } + elements.swapAt(lo, hi) + } + + + return lo +} + +private func findLo(in array: [T], pivot: T, from lo: inout Int, to hi: Int, by areInIncreasingOrder: (T, T)->Bool) -> Bool { + while lo != hi { + if !areInIncreasingOrder(array[lo], pivot) { + return true + } + array.formIndex(after: &lo) + } + return false +} + +private func findHi(in array: [T], pivot: T, from lo: Int, to hi: inout Int, by areInIncreasingOrder: (T, T)->Bool) -> Bool { + while hi != lo { + if areInIncreasingOrder(array[hi], pivot) { return true } + array.formIndex(before: &hi) + } + return false +} diff --git a/Introsort/README.markdown b/Introsort/README.markdown new file mode 100644 index 000000000..280950928 --- /dev/null +++ b/Introsort/README.markdown @@ -0,0 +1,113 @@ +# IntroSort + +Goal: Sort an array from low to high (or high to low). + +IntroSort is the algorithm used by Swift to sort a collection. Introsort is an hybrid algorithm invented by David Musser in 1993 with the purpose of giving a generic sorting algorithm for the C++ standard library. + +The classic implementation of introsort uses a recursive Quicksort with a fallback to Heapsort in the case where the recursion depth level reached a certain maximum value. The maximum depends on the number of elements in the collection and it is usually 2 * log(n). The reason behind this “fallback” is that if Quicksort was not able to get the solution after 2 * log(n) recursions for a branch, probably it hit its worst case and it is degrading to complexity O( n^2 ). To optimise even further this algorithm, the Swift implementation introduce an extra step in each recursion where the partition is sorted using InsertionSort if the count of the partition is less than 20. + +The number 20 is an empiric number obtained observing the behaviour of InsertionSort with lists of this size. + +Here's an implementation in pseudocode: + +``` +procedure sort(A : array): + let maxdepth = ⌊log(length(A))⌋ × 2 + introSort(A, maxdepth) + +procedure introsort(A, maxdepth): + n ← length(A) + if n < 20: + insertionsort(A) + else if maxdepth = 0: + heapsort(A) + else: + p ← partition(A) // the pivot is selected using median of 3 + introsort(A[0:p], maxdepth - 1) + introsort(A[p+1:n], maxdepth - 1) +``` + +## An example + +Let's walk through the example. The array is initially: + + [ 10, 0, 3, 9, 2, 14, 8, 27, 1, 5, 8, -1, 26 ] + + +For this example let's assume that `maxDepth` is **2** and that the size of the partition for the insertionSort to kick in is **5** + +The first iteration of introsort begins by attempting to use insertionSort. The collection has 13 elements, so it tries to do heapsort instead. The condition for heapsort to occur is if `maxdepth == 0` evaluates true. Since `maxdepth` is currently **2** for the first iteration, introsort will default to quicksort. + +The `partition` method picks the first element, the median and the last, it sorts them and uses the new median as pivot. + + [ 10, 8, 26 ] -> [ 8, 10, 26 ] + +Our array is now + + [ 8, 0, 3, 9, 2, 14, 10, 27, 1, 5, 8, -1, 26 ] + +**10** is the pivot. After the choice of the pivot, the `partition` method swaps elements to get all the elements less than pivot on the left, and all the elements more or equal than pivot on the right. + + [ 8, 0, 3, 9, 2, 1, 5, 8, -1, 10, 27, 14, 26 ] + +Because of the swaps, the index of of pivot is now changed and returned. The next step of introsort is to call recursively itself for the two sub arrays: + + less: [ 8, 0, 3, 9, 2, 1, 5, 8, -1, 10 ] + greater: [ 27, 14, 26 ] + +## maxDepth: 1, branch: less + + [ 8, 0, 3, 9, 2, 1, 5, 8, -1, 10 ] + +The count of the array is still more than 5 so we don't meet yet the conditions for insertion sort to kick in. At this iteration maxDepth is decreased by one but it is still more than zero, so heapsort will not act. + +Just like in the previous iteration quicksort wins and the `partition` method choses a pivot and sorts the elemets less than pivot on the left and the elements more or equeal than pivot on the right. + + array: [ 8, 0, 3, 9, 2, 1, 5, 8, -1, 10 ] + pivot candidates: [ 8, 1, 10] -> [ 1, 8, 10] + pivot: 8 + before partition: [ 1, 0, 3, 9, 2, 8, 5, 8, -1, 10 ] + after partition: [ 1, 0, 3, -1, 2, 5, 8, 8, 9, 10 ] + + less: [ 1, 0, 3, -1, 2, 5, 8 ] + greater: [ 8, 9, 10 ] + +## maxDepth: 0, branch: less + + [ 1, 0, 3, -1, 2, 5, 8 ] + +Just like before, introsort is recursively executed for `less` and greater. This time `less`has a count more than **5** so it will not be sorted with insertion sort, but the maxDepth decreased again by 1 is now 0 and heapsort takes over sorting the array. + + heapsort -> [ -1, 0, 1, 2, 3, 5, 8 ] + +## maxDepth: 0, branch: greater + + [ 8, 9, 10 ] + +following greater in this recursion, the count of elements is 3, which is less than 5, so this partition is sorted using insertionSort. + + insertionSort -> [ 8, 9 , 10] + + +## back to maxDepth = 1, branch: greater + + [ 27, 14, 26 ] + +At this point the original array has mutated to be + + [ -1, 0, 1, 2, 3, 5, 8, 8, 9, 10, 27, 14, 26 ] + +now the `less` partition is sorted and since the count of the `greater` partition is 3 it will be sorted with insertion sort `[ 14, 26, 27 ]` + +The array is now successfully sorted + + [ -1, 0, 1, 2, 3, 5, 8, 8, 9, 10, 14, 26, 27 ] + + +## See also + +[Introsort on Wikipedia](https://en.wikipedia.org/wiki/Introsort) +[Introsort comparison with other sorting algorithms](http://agostini.tech/2017/12/18/swift-sorting-algorithm/) +[Introsort implementation from the Swift standard library](https://github.com/apple/swift/blob/09f77ff58d250f5d62855ea359fc304f40b531df/stdlib/public/core/Sort.swift.gyb) + +*Written for Swift Algorithm Club by Giuseppe Lanza* diff --git a/Introsort/Randomize.swift b/Introsort/Randomize.swift new file mode 100644 index 000000000..6d1d57132 --- /dev/null +++ b/Introsort/Randomize.swift @@ -0,0 +1,9 @@ +import Foundation + +public func randomize(n: Int) -> [Int] { + var unsorted = [Int]() + for _ in 0..(in array: inout [T], a: Int, b: Int, c: Int, by areInIncreasingOrder: (T, T) -> Bool) { + switch (areInIncreasingOrder(array[b], array[a]), + areInIncreasingOrder(array[c], array[b])) { + case (false, false): break + case (true, true): array.swapAt(a, c) + case (true, false): + array.swapAt(a, b) + + if areInIncreasingOrder(array[c], array[b]) { + array.swapAt(b, c) + } + case (false, true): + array.swapAt(b, c) + + if areInIncreasingOrder(array[b], array[a]) { + array.swapAt(a, b) + } + } +} diff --git a/K-Means/KMeans.swift b/K-Means/KMeans.swift index 4a89a3fae..bccc6efd3 100644 --- a/K-Means/KMeans.swift +++ b/K-Means/KMeans.swift @@ -12,7 +12,7 @@ class KMeans { } private func indexOfNearestCenter(_ x: Vector, centers: [Vector]) -> Int { - var nearestDist = DBL_MAX + var nearestDist = Double.greatestFiniteMagnitude var minIndex = 0 for (idx, center) in centers.enumerated() { diff --git a/K-Means/Tests/KMeansTests.swift b/K-Means/Tests/KMeansTests.swift index e6f4b08a1..7e0c93004 100644 --- a/K-Means/Tests/KMeansTests.swift +++ b/K-Means/Tests/KMeansTests.swift @@ -10,13 +10,6 @@ import Foundation import XCTest class KMeansTests: XCTestCase { - func testSwift4() { - // last checked with Xcode 9.0b4 - #if swift(>=4.0) - print("Hello, Swift 4!") - #endif - } - func genPoints(_ numPoints: Int, numDimensions: Int) -> [Vector] { var points = [Vector]() for _ in 0.. + + + + IDEDidComputeMac32BitWarning + + + diff --git a/K-Means/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme b/K-Means/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme index 2b401e86e..73ba8f37d 100644 --- a/K-Means/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme +++ b/K-Means/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme @@ -1,6 +1,6 @@ =4.0) -print("Hello, Swift 4") -#endif + precedencegroup ExponentiativePrecedence { higherThan: MultiplicationPrecedence lowerThan: BitwiseShiftPrecedence @@ -18,8 +15,8 @@ func ^^ (radix: Int, power: Int) -> Int { // Long Multiplication - O(n^2) func multiply(_ num1: Int, by num2: Int, base: Int = 10) -> Int { - let num1Array = String(num1).characters.reversed().map { Int(String($0))! } - let num2Array = String(num2).characters.reversed().map { Int(String($0))! } + let num1Array = String(num1).reversed().map { Int(String($0))! } + let num2Array = String(num2).reversed().map { Int(String($0))! } var product = Array(repeating: 0, count: num1Array.count + num2Array.count) @@ -38,14 +35,14 @@ func multiply(_ num1: Int, by num2: Int, base: Int = 10) -> Int { // Karatsuba Multiplication - O(n^log2(3)) func karatsuba(_ num1: Int, by num2: Int) -> Int { - let num1Array = String(num1).characters - let num2Array = String(num2).characters + let num1String = String(num1) + let num2String = String(num2) - guard num1Array.count > 1 && num2Array.count > 1 else { + guard num1String.count > 1 && num2String.count > 1 else { return multiply(num1, by: num2) } - let n = max(num1Array.count, num2Array.count) + let n = max(num1String.count, num2String.count) let nBy2 = n / 2 let a = num1 / 10^^nBy2 diff --git a/Karatsuba Multiplication/KaratsubaMultiplication.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Karatsuba Multiplication/KaratsubaMultiplication.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Karatsuba Multiplication/KaratsubaMultiplication.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Karatsuba Multiplication/KaratsubaMultiplication.swift b/Karatsuba Multiplication/KaratsubaMultiplication.swift index fb20a667d..3d14a243a 100644 --- a/Karatsuba Multiplication/KaratsubaMultiplication.swift +++ b/Karatsuba Multiplication/KaratsubaMultiplication.swift @@ -19,27 +19,48 @@ func ^^ (radix: Int, power: Int) -> Int { return Int(pow(Double(radix), Double(power))) } +// Long Multiplication - O(n^2) +func multiply(_ num1: Int, by num2: Int, base: Int = 10) -> Int { + let num1Array = String(num1).reversed().map { Int(String($0))! } + let num2Array = String(num2).reversed().map { Int(String($0))! } + + var product = Array(repeating: 0, count: num1Array.count + num2Array.count) + + for i in num1Array.indices { + var carry = 0 + for j in num2Array.indices { + product[i + j] += carry + num1Array[i] * num2Array[j] + carry = product[i + j] / base + product[i + j] %= base + } + product[i + num2Array.count] += carry + } + + return Int(product.reversed().map { String($0) }.reduce("", +))! +} + +// Karatsuba Multiplication - O(n^log2(3)) func karatsuba(_ num1: Int, by num2: Int) -> Int { - let num1Array = String(num1).characters - let num2Array = String(num2).characters - - guard num1Array.count > 1 && num2Array.count > 1 else { - return num1 * num2 + let num1String = String(num1) + let num2String = String(num2) + + guard num1String.count > 1 && num2String.count > 1 else { + return multiply(num1, by: num2) } - - let n = max(num1Array.count, num2Array.count) + + let n = max(num1String.count, num2String.count) let nBy2 = n / 2 - + let a = num1 / 10^^nBy2 let b = num1 % 10^^nBy2 let c = num2 / 10^^nBy2 let d = num2 % 10^^nBy2 - + let ac = karatsuba(a, by: c) let bd = karatsuba(b, by: d) let adPlusbc = karatsuba(a+b, by: c+d) - ac - bd - + let product = ac * 10^^(2 * nBy2) + adPlusbc * 10^^nBy2 + bd - + return product } diff --git a/Karatsuba Multiplication/README.markdown b/Karatsuba Multiplication/README.markdown index c86f69831..02e54a1bf 100644 --- a/Karatsuba Multiplication/README.markdown +++ b/Karatsuba Multiplication/README.markdown @@ -72,14 +72,14 @@ Here's the full implementation. Note that the recursive algorithm is most effici ```swift // Karatsuba Multiplication func karatsuba(_ num1: Int, by num2: Int) -> Int { - let num1Array = String(num1).characters - let num2Array = String(num2).characters + let num1String = String(num1) + let num2String = String(num2) - guard num1Array.count > 1 && num2Array.count > 1 else { - return num1*num2 + guard num1String.count > 1 && num2String.count > 1 else { + return multiply(num1, by: num2) } - let n = max(num1Array.count, num2Array.count) + let n = max(num1String.count, num2String.count) let nBy2 = n / 2 let a = num1 / 10^^nBy2 @@ -115,10 +115,10 @@ What about the running time of this algorithm? Is all this extra work worth it? ## Resources -[Wikipedia] (https://en.wikipedia.org/wiki/Karatsuba_algorithm) +[Wikipedia](https://en.wikipedia.org/wiki/Karatsuba_algorithm) -[WolframMathWorld] (http://mathworld.wolfram.com/KaratsubaMultiplication.html) +[WolframMathWorld](http://mathworld.wolfram.com/KaratsubaMultiplication.html) -[Master Theorem] (https://en.wikipedia.org/wiki/Master_theorem) +[Master Theorem](https://en.wikipedia.org/wiki/Master_theorem) *Written for Swift Algorithm Club by Richard Ash* diff --git a/Karatsuba Multiplication/Tests/KaratsubaMultiplicationTests.swift b/Karatsuba Multiplication/Tests/KaratsubaMultiplicationTests.swift new file mode 100644 index 000000000..379c9e812 --- /dev/null +++ b/Karatsuba Multiplication/Tests/KaratsubaMultiplicationTests.swift @@ -0,0 +1,17 @@ +// +// KaratsubaMultiplicationTests.swift +// Tests +// +// Created by Afonso Graça on 8/10/18. +// + +import XCTest + +final class KaratsubaMultiplicationTests: XCTestCase { + + func testReadmeExample() { + let subject = karatsuba(1234, by: 5678) + + XCTAssertEqual(subject, 7006652) + } +} diff --git a/Karatsuba Multiplication/Tests/Tests.xcodeproj/project.pbxproj b/Karatsuba Multiplication/Tests/Tests.xcodeproj/project.pbxproj new file mode 100644 index 000000000..ef23067ee --- /dev/null +++ b/Karatsuba Multiplication/Tests/Tests.xcodeproj/project.pbxproj @@ -0,0 +1,305 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 6AF099AC216B54E200F69B16 /* KaratsubaMultiplication.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AF099AB216B54E200F69B16 /* KaratsubaMultiplication.swift */; }; + 6AF099AE216B55A100F69B16 /* KaratsubaMultiplicationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AF099AD216B55A100F69B16 /* KaratsubaMultiplicationTests.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 6AF099A2216B54D500F69B16 /* Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 6AF099A7216B54D500F69B16 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Tests/Info.plist; sourceTree = SOURCE_ROOT; }; + 6AF099AB216B54E200F69B16 /* KaratsubaMultiplication.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = KaratsubaMultiplication.swift; path = ../KaratsubaMultiplication.swift; sourceTree = ""; }; + 6AF099AD216B55A100F69B16 /* KaratsubaMultiplicationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KaratsubaMultiplicationTests.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 6AF0999F216B54D500F69B16 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 6AF09997216B545D00F69B16 = { + isa = PBXGroup; + children = ( + 6AF099AB216B54E200F69B16 /* KaratsubaMultiplication.swift */, + 6AF099A4216B54D500F69B16 /* Tests */, + 6AF099A3216B54D500F69B16 /* Products */, + ); + sourceTree = ""; + }; + 6AF099A3216B54D500F69B16 /* Products */ = { + isa = PBXGroup; + children = ( + 6AF099A2216B54D500F69B16 /* Tests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 6AF099A4216B54D500F69B16 /* Tests */ = { + isa = PBXGroup; + children = ( + 6AF099AD216B55A100F69B16 /* KaratsubaMultiplicationTests.swift */, + 6AF099A7216B54D500F69B16 /* Info.plist */, + ); + name = Tests; + sourceTree = SOURCE_ROOT; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 6AF099A1216B54D500F69B16 /* Tests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 6AF099A8216B54D500F69B16 /* Build configuration list for PBXNativeTarget "Tests" */; + buildPhases = ( + 6AF0999E216B54D500F69B16 /* Sources */, + 6AF0999F216B54D500F69B16 /* Frameworks */, + 6AF099A0216B54D500F69B16 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Tests; + productName = Tests; + productReference = 6AF099A2216B54D500F69B16 /* Tests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 6AF09998216B545D00F69B16 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1000; + LastUpgradeCheck = 1000; + TargetAttributes = { + 6AF099A1216B54D500F69B16 = { + CreatedOnToolsVersion = 10.0; + }; + }; + }; + buildConfigurationList = 6AF0999B216B545D00F69B16 /* Build configuration list for PBXProject "Tests" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 6AF09997216B545D00F69B16; + productRefGroup = 6AF099A3216B54D500F69B16 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 6AF099A1216B54D500F69B16 /* Tests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 6AF099A0216B54D500F69B16 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 6AF0999E216B54D500F69B16 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 6AF099AE216B55A100F69B16 /* KaratsubaMultiplicationTests.swift in Sources */, + 6AF099AC216B54E200F69B16 /* KaratsubaMultiplication.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 6AF0999C216B545D00F69B16 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Debug; + }; + 6AF0999D216B545D00F69B16 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + }; + name = Release; + }; + 6AF099A9216B54D500F69B16 /* 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; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + 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; + INFOPLIST_FILE = Tests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_BUNDLE_IDENTIFIER = swift.algorithm.club.Tests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 4.2; + }; + name = Debug; + }; + 6AF099AA216B54D500F69B16 /* 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; + CODE_SIGN_IDENTITY = "-"; + CODE_SIGN_STYLE = Automatic; + COMBINE_HIDPI_IMAGES = YES; + COPY_PHASE_STRIP = NO; + 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; + INFOPLIST_FILE = Tests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); + MACOSX_DEPLOYMENT_TARGET = 10.14; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = swift.algorithm.club.Tests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 4.2; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 6AF0999B216B545D00F69B16 /* Build configuration list for PBXProject "Tests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6AF0999C216B545D00F69B16 /* Debug */, + 6AF0999D216B545D00F69B16 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 6AF099A8216B54D500F69B16 /* Build configuration list for PBXNativeTarget "Tests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 6AF099A9216B54D500F69B16 /* Debug */, + 6AF099AA216B54D500F69B16 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 6AF09998216B545D00F69B16 /* Project object */; +} diff --git a/Karatsuba Multiplication/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme b/Karatsuba Multiplication/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme new file mode 100644 index 000000000..1288fd9a8 --- /dev/null +++ b/Karatsuba Multiplication/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Karatsuba Multiplication/Tests/Tests/Info.plist b/Karatsuba Multiplication/Tests/Tests/Info.plist new file mode 100644 index 000000000..6c40a6cd0 --- /dev/null +++ b/Karatsuba Multiplication/Tests/Tests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Knuth-Morris-Pratt/KnuthMorrisPratt.playground/Contents.swift b/Knuth-Morris-Pratt/KnuthMorrisPratt.playground/Contents.swift index 6609c9abe..9b4a50fa0 100644 --- a/Knuth-Morris-Pratt/KnuthMorrisPratt.playground/Contents.swift +++ b/Knuth-Morris-Pratt/KnuthMorrisPratt.playground/Contents.swift @@ -1,13 +1,8 @@ //: Playground - noun: a place where people can play -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift4!") -#endif - func ZetaAlgorithm(ptnr: String) -> [Int]? { - let pattern = Array(ptnr.characters) + let pattern = Array(ptnr) let patternLength: Int = pattern.count guard patternLength > 0 else { @@ -65,7 +60,7 @@ extension String { func indexesOf(ptnr: String) -> [Int]? { - let text = Array(self.characters) + let text = Array(self) let pattern = Array(ptnr.characters) let textLength: Int = text.count diff --git a/Knuth-Morris-Pratt/KnuthMorrisPratt.swift b/Knuth-Morris-Pratt/KnuthMorrisPratt.swift index 81bc65278..be0cd961d 100644 --- a/Knuth-Morris-Pratt/KnuthMorrisPratt.swift +++ b/Knuth-Morris-Pratt/KnuthMorrisPratt.swift @@ -12,8 +12,8 @@ extension String { func indexesOf(ptnr: String) -> [Int]? { - let text = Array(self.characters) - let pattern = Array(ptnr.characters) + let text = Array(self) + let pattern = Array(ptnr) let textLength: Int = text.count let patternLength: Int = pattern.count diff --git a/Knuth-Morris-Pratt/README.markdown b/Knuth-Morris-Pratt/README.markdown index 95fdea9e9..2e4d5360b 100644 --- a/Knuth-Morris-Pratt/README.markdown +++ b/Knuth-Morris-Pratt/README.markdown @@ -14,7 +14,7 @@ let concert = "🎼🎹🎹🎸🎸🎻🎻🎷🎺🎤👏👏👏" concert.indexesOf(ptnr: "🎻🎷") // Output: [6] ``` -The [Knuth-Morris-Pratt algorithm](https://en.wikipedia.org/wiki/Knuth–Morris–Pratt_algorithm) is considered one of the best algorithms for solving the pattern matching problem. Although in practice [Boyer-Moore](../Boyer-Moore/) is usually preferred, the algorithm that we will introduce is simpler, and has the same (linear) running time. +The [Knuth-Morris-Pratt algorithm](https://en.wikipedia.org/wiki/Knuth–Morris–Pratt_algorithm) is considered one of the best algorithms for solving the pattern matching problem. Although in practice [Boyer-Moore](../Boyer-Moore-Horspool/) is usually preferred, the algorithm that we will introduce is simpler, and has the same (linear) running time. The idea behind the algorithm is not too different from the [naive string search](../Brute-Force%20String%20Search/) procedure. As it, Knuth-Morris-Pratt aligns the text with the pattern and goes with character comparisons from left to right. But, instead of making a shift of one character when a mismatch occurs, it uses a more intelligent way to move the pattern along the text. In fact, the algorithm features a pattern pre-processing stage where it acquires all the informations that will make the algorithm skip redundant comparisons, resulting in larger shifts. diff --git a/Kth Largest Element/README.markdown b/Kth Largest Element/README.markdown index aba2d9c29..b23d22b74 100644 --- a/Kth Largest Element/README.markdown +++ b/Kth Largest Element/README.markdown @@ -40,7 +40,7 @@ Now, all we must do is take the value at index `a.count - k`: a[a.count - k] = a[8 - 4] = a[4] = 9 ``` -Of course, if you were looking for the k-th *smallest* element, you'd use `a[k]`. +Of course, if you were looking for the k-th *smallest* element, you'd use `a[k-1]`. ## A faster solution @@ -84,29 +84,29 @@ The index of pivot `9` is 4, and that's exactly the *k* we're looking for. We're The following function implements these ideas: ```swift -public func randomizedSelect(array: [T], order k: Int) -> T { +public func randomizedSelect(_ array: [T], order k: Int) -> T { var a = array - func randomPivot(inout a: [T], _ low: Int, _ high: Int) -> T { + func randomPivot(_ a: inout [T], _ low: Int, _ high: Int) -> T { let pivotIndex = random(min: low, max: high) - swap(&a, pivotIndex, high) + a.swapAt(pivotIndex, high) return a[high] } - func randomizedPartition(inout a: [T], _ low: Int, _ high: Int) -> Int { + func randomizedPartition(_ a: inout [T], _ low: Int, _ high: Int) -> Int { let pivot = randomPivot(&a, low, high) var i = low for j in low..(inout a: [T], _ low: Int, _ high: Int, _ k: Int) -> T { + func randomizedSelect(_ a: inout [T], _ low: Int, _ high: Int, _ k: Int) -> T { if low < high { let p = randomizedPartition(&a, low, high) if k == p { diff --git a/Kth Largest Element/kthLargest.playground/Contents.swift b/Kth Largest Element/kthLargest.playground/Contents.swift index e6fca2ceb..a27dfce7d 100644 --- a/Kth Largest Element/kthLargest.playground/Contents.swift +++ b/Kth Largest Element/kthLargest.playground/Contents.swift @@ -1,9 +1,5 @@ //: Playground - noun: a place where people can play -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif let a = [5, 1, 3, 2, 7, 6, 4] diff --git a/Kth Largest Element/kthLargest.playground/Sources/kthLargest.swift b/Kth Largest Element/kthLargest.playground/Sources/kthLargest.swift index 397c43a38..aad15eac6 100644 --- a/Kth Largest Element/kthLargest.playground/Sources/kthLargest.swift +++ b/Kth Largest Element/kthLargest.playground/Sources/kthLargest.swift @@ -16,12 +16,6 @@ public func kthLargest(_ a: [Int], _ k: Int) -> Int? { // MARK: - Randomized selection -/* Returns a random integer in the range min...max, inclusive. */ -public func random(min: Int, max: Int) -> Int { - assert(min < max) - return min + Int(arc4random_uniform(UInt32(max - min + 1))) -} - /* Returns the i-th smallest element from the array. @@ -39,7 +33,7 @@ public func randomizedSelect(_ array: [T], order k: Int) -> T { var a = array func randomPivot(_ a: inout [T], _ low: Int, _ high: Int) -> T { - let pivotIndex = random(min: low, max: high) + let pivotIndex = Int.random(in: low...high) a.swapAt(pivotIndex, high) return a[high] } diff --git a/LRU Cache/LRUCache.playground/Contents.swift b/LRU Cache/LRUCache.playground/Contents.swift index 2ebc03242..35dcc5d59 100644 --- a/LRU Cache/LRUCache.playground/Contents.swift +++ b/LRU Cache/LRUCache.playground/Contents.swift @@ -1,8 +1,3 @@ -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - let cache = LRUCache(2) cache.set("a", val: 1) cache.set("b", val: 2) diff --git a/LRU Cache/LRUCache.playground/playground.xcworkspace/contents.xcworkspacedata b/LRU Cache/LRUCache.playground/playground.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/LRU Cache/LRUCache.playground/playground.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/LRU Cache/LRUCache.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/LRU Cache/LRUCache.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/LRU Cache/LRUCache.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Linear Regression/LinearRegression.playground/Contents.swift b/Linear Regression/LinearRegression.playground/Contents.swift index 3e4135794..a093ef566 100644 --- a/Linear Regression/LinearRegression.playground/Contents.swift +++ b/Linear Regression/LinearRegression.playground/Contents.swift @@ -2,11 +2,6 @@ import Foundation -// last checked with Xcode 4.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - let carAge: [Double] = [10, 8, 3, 3, 2, 1] let carPrice: [Double] = [500, 400, 7000, 8500, 11000, 10500] var intercept = 0.0 @@ -22,7 +17,7 @@ let numberOfCarAdvertsWeSaw = carPrice.count let numberOfIterations = 100 let alpha = 0.0001 -for n in 1...numberOfIterations { +for _ in 1...numberOfIterations { for i in 0..=4.0) -print("Hello, Swift 4!") -#endif - func linearSearch(_ array: [T], _ object: T) -> Int? { for (index, obj) in array.enumerated() where obj == object { return index diff --git a/Linked List/LinkedList.playground/Contents.swift b/Linked List/LinkedList.playground/Contents.swift index b78ee4809..76b911310 100644 --- a/Linked List/LinkedList.playground/Contents.swift +++ b/Linked List/LinkedList.playground/Contents.swift @@ -3,345 +3,372 @@ // For best results, don't forget to select "Show Rendered Markup" from XCode's "Editor" menu //: Linked List Class Declaration: -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif public final class LinkedList { - - /// Linked List's Node Class Declaration - public class LinkedListNode { - var value: T - var next: LinkedListNode? - weak var previous: LinkedListNode? - - public init(value: T) { - self.value = value + + /// Linked List's Node Class Declaration + public class LinkedListNode { + var value: T + var next: LinkedListNode? + weak var previous: LinkedListNode? + + public init(value: T) { + self.value = value + } + } + + /// Typealiasing the node class to increase readability of code + public typealias Node = LinkedListNode + + + /// The head of the Linked List + private(set) var head: Node? + + /// Computed property to iterate through the linked list and return the last node in the list (if any) + public var last: Node? { + guard var node = head else { + return nil + } + + while let next = node.next { + node = next + } + return node + } + + /// Computed property to check if the linked list is empty + public var isEmpty: Bool { + return head == nil + } + + /// Computed property to iterate through the linked list and return the total number of nodes + public var count: Int { + guard var node = head else { + return 0 + } + + var count = 1 + while let next = node.next { + node = next + count += 1 + } + return count + } + + /// Default initializer + public init() {} + + + /// Subscript function to return the node at a specific index + /// + /// - Parameter index: Integer value of the requested value's index + public subscript(index: Int) -> T { + let node = self.node(at: index) + return node.value + } + + /// Function to return the node at a specific index. Crashes if index is out of bounds (0...self.count) + /// + /// - Parameter index: Integer value of the node's index to be returned + /// - Returns: LinkedListNode + public func node(at index: Int) -> Node { + assert(head != nil, "List is empty") + assert(index >= 0, "index must be greater than 0") + + if index == 0 { + return head! + } else { + var node = head!.next + for _ in 1.. - - /// The head of the Linked List - fileprivate var head: Node? - - /// Default initializer - public init() {} - - /// Computed property to check if the linked list is empty - public var isEmpty: Bool { - return head == nil - } - - /// Computed property to get the first node in the linked list (if any) - public var first: Node? { - return head - } - - /// Computed property to iterate through the linked list and return the last node in the list (if any) - public var last: Node? { - if var node = head { - while case let next? = node.next { - node = next - } - return node - } else { - return nil + + /// Append a copy of a LinkedListNode to the end of the list. + /// + /// - Parameter node: The node containing the value to be appended + public func append(_ node: Node) { + let newNode = node + if let lastNode = last { + newNode.previous = lastNode + lastNode.next = newNode + } else { + head = newNode + } } - } - - /// Computed property to iterate through the linked list and return the total number of nodes - public var count: Int { - if var node = head { - var c = 1 - while case let next? = node.next { - node = next - c += 1 - } - return c - } else { - return 0 + + /// Append a copy of a LinkedList to the end of the list. + /// + /// - Parameter list: The list to be copied and appended. + public func append(_ list: LinkedList) { + var nodeToCopy = list.head + while let node = nodeToCopy { + append(node.value) + nodeToCopy = node.next + } } - } - - /// Function to return the node at a specific index. Crashes if index is out of bounds (0...self.count) - /// - /// - Parameter index: Integer value of the node's index to be returned - /// - Returns: Optional LinkedListNode - public func node(atIndex index: Int) -> Node? { - if index >= 0 { - var node = head - var i = index - while node != nil { - if i == 0 { return node } - i -= 1 - node = node!.next - } + + /// Insert a value at a specific index. Crashes if index is out of bounds (0...self.count) + /// + /// - Parameters: + /// - value: The data value to be inserted + /// - index: Integer value of the index to be insterted at + public func insert(_ value: T, at index: Int) { + let newNode = Node(value: value) + insert(newNode, at: index) } - return nil - } - - /// Subscript function to return the node at a specific index - /// - /// - Parameter index: Integer value of the requested value's index - public subscript(index: Int) -> T { - let node = self.node(atIndex: index) - assert(node != nil) - return node!.value - } - - /// Append a value to the end of the list - /// - /// - Parameter value: The data value to be appended - public func append(_ value: T) { - let newNode = Node(value: value) - self.append(newNode) - } - - /// Append a copy of a LinkedListNode to the end of the list. - /// - /// - Parameter node: The node containing the value to be appended - public func append(_ node: Node) { - let newNode = LinkedListNode(value: node.value) - if let lastNode = last { - newNode.previous = lastNode - lastNode.next = newNode - } else { - head = newNode + + /// Insert a copy of a node at a specific index. Crashes if index is out of bounds (0...self.count) + /// + /// - Parameters: + /// - node: The node containing the value to be inserted + /// - index: Integer value of the index to be inserted at + public func insert(_ newNode: Node, at index: Int) { + if index == 0 { + newNode.next = head + head?.previous = newNode + head = newNode + } else { + let prev = node(at: index - 1) + let next = prev.next + newNode.previous = prev + newNode.next = next + next?.previous = newNode + prev.next = newNode + } } - } - - /// Append a copy of a LinkedList to the end of the list. - /// - /// - Parameter list: The list to be copied and appended. - public func append(_ list: LinkedList) { - var nodeToCopy = list.head - while let node = nodeToCopy { - self.append(node.value) - nodeToCopy = node.next + + /// Insert a copy of a LinkedList at a specific index. Crashes if index is out of bounds (0...self.count) + /// + /// - Parameters: + /// - list: The LinkedList to be copied and inserted + /// - index: Integer value of the index to be inserted at + public func insert(_ list: LinkedList, at index: Int) { + if list.isEmpty { return } + + if index == 0 { + list.last?.next = head + head = list.head + } else { + let prev = node(at: index - 1) + let next = prev.next + + prev.next = list.head + list.head?.previous = prev + + list.last?.next = next + next?.previous = list.last + } } - } - - /// A private helper funciton to find the nodes before and after a specified index. Crashes if index is out of bounds (0...self.count) - /// - /// - Parameter index: Integer value of the index between the nodes. - /// - Returns: A tuple of 2 nodes before & after the specified index respectively. - private func nodesBeforeAndAfter(index: Int) -> (Node?, Node?) { - assert(index >= 0) - - var i = index - var next = head - var prev: Node? - - while next != nil && i > 0 { - i -= 1 - prev = next - next = next!.next + + /// Function to remove all nodes/value from the list + public func removeAll() { + head = nil } - assert(i == 0) // if > 0, then specified index was too large - - return (prev, next) - } - - /// Insert a value at a specific index. Crashes if index is out of bounds (0...self.count) - /// - /// - Parameters: - /// - value: The data value to be inserted - /// - index: Integer value of the index to be insterted at - public func insert(_ value: T, atIndex index: Int) { - let newNode = Node(value: value) - self.insert(newNode, atIndex: index) - } - - /// Insert a copy of a node at a specific index. Crashes if index is out of bounds (0...self.count) - /// - /// - Parameters: - /// - node: The node containing the value to be inserted - /// - index: Integer value of the index to be inserted at - public func insert(_ node: Node, atIndex index: Int) { - let (prev, next) = nodesBeforeAndAfter(index: index) - let newNode = LinkedListNode(value: node.value) - newNode.previous = prev - newNode.next = next - prev?.next = newNode - next?.previous = newNode - - if prev == nil { - head = newNode + + /// Function to remove a specific node. + /// + /// - Parameter node: The node to be deleted + /// - Returns: The data value contained in the deleted node. + @discardableResult public func remove(node: Node) -> T { + let prev = node.previous + let next = node.next + + if let prev = prev { + prev.next = next + } else { + head = next + } + next?.previous = prev + + node.previous = nil + node.next = nil + return node.value } - } - - /// Insert a copy of a LinkedList at a specific index. Crashes if index is out of bounds (0...self.count) - /// - /// - Parameters: - /// - list: The LinkedList to be copied and inserted - /// - index: Integer value of the index to be inserted at - public func insert(_ list: LinkedList, atIndex index: Int) { - if list.isEmpty { return } - var (prev, next) = nodesBeforeAndAfter(index: index) - var nodeToCopy = list.head - var newNode: Node? - while let node = nodeToCopy { - newNode = Node(value: node.value) - newNode?.previous = prev - if let previous = prev { - previous.next = newNode - } else { - self.head = newNode - } - nodeToCopy = nodeToCopy?.next - prev = newNode + + /// Function to remove the last node/value in the list. Crashes if the list is empty + /// + /// - Returns: The data value contained in the deleted node. + @discardableResult public func removeLast() -> T { + assert(!isEmpty) + return remove(node: last!) } - prev?.next = next - next?.previous = prev - } - - /// Function to remove all nodes/value from the list - public func removeAll() { - head = nil - } - - /// Function to remove a specific node. - /// - /// - Parameter node: The node to be deleted - /// - Returns: The data value contained in the deleted node. - @discardableResult public func remove(node: Node) -> T { - let prev = node.previous - let next = node.next - - if let prev = prev { - prev.next = next - } else { - head = next + + /// Function to remove a node/value at a specific index. Crashes if index is out of bounds (0...self.count) + /// + /// - Parameter index: Integer value of the index of the node to be removed + /// - Returns: The data value contained in the deleted node + @discardableResult public func remove(at index: Int) -> T { + let node = self.node(at: index) + return remove(node: node) } - next?.previous = prev - - node.previous = nil - node.next = nil - return node.value - } - - /// Function to remove the last node/value in the list. Crashes if the list is empty - /// - /// - Returns: The data value contained in the deleted node. - @discardableResult public func removeLast() -> T { - assert(!isEmpty) - return remove(node: last!) - } - - /// Function to remove a node/value at a specific index. Crashes if index is out of bounds (0...self.count) - /// - /// - Parameter index: Integer value of the index of the node to be removed - /// - Returns: The data value contained in the deleted node - @discardableResult public func remove(atIndex index: Int) -> T { - let node = self.node(atIndex: index) - assert(node != nil) - return remove(node: node!) - } } //: End of the base class declarations & beginning of extensions' declarations: // MARK: - Extension to enable the standard conversion of a list to String extension LinkedList: CustomStringConvertible { - public var description: String { - var s = "[" - var node = head - while node != nil { - s += "\(node!.value)" - node = node!.next - if node != nil { s += ", " } + public var description: String { + var s = "[" + var node = head + while let nd = node { + s += "\(nd.value)" + node = nd.next + if node != nil { s += ", " } + } + return s + "]" } - return s + "]" - } } // MARK: - Extension to add a 'reverse' function to the list extension LinkedList { - public func reverse() { - var node = head - while let currentNode = node { - node = currentNode.next - swap(¤tNode.next, ¤tNode.previous) - head = currentNode + public func reverse() { + var node = head + while let currentNode = node { + node = currentNode.next + swap(¤tNode.next, ¤tNode.previous) + head = currentNode + } } - } } // MARK: - An extension with an implementation of 'map' & 'filter' functions extension LinkedList { - public func map(transform: (T) -> U) -> LinkedList { - let result = LinkedList() - var node = head - while node != nil { - result.append(transform(node!.value)) - node = node!.next + public func map(transform: (T) -> U) -> LinkedList { + let result = LinkedList() + var node = head + while let nd = node { + result.append(transform(nd.value)) + node = nd.next + } + return result } - return result - } - - public func filter(predicate: (T) -> Bool) -> LinkedList { - let result = LinkedList() - var node = head - while node != nil { - if predicate(node!.value) { - result.append(node!.value) - } - node = node!.next + + public func filter(predicate: (T) -> Bool) -> LinkedList { + let result = LinkedList() + var node = head + while let nd = node { + if predicate(nd.value) { + result.append(nd.value) + } + node = nd.next + } + return result } - return result - } } // MARK: - Extension to enable initialization from an Array extension LinkedList { - convenience init(array: Array) { - self.init() - - for element in array { - self.append(element) + convenience init(array: Array) { + self.init() + + array.forEach { append($0) } } - } } // MARK: - Extension to enable initialization from an Array Literal extension LinkedList: ExpressibleByArrayLiteral { - public convenience init(arrayLiteral elements: T...) { - self.init() + public convenience init(arrayLiteral elements: T...) { + self.init() + + elements.forEach { append($0) } + } +} - for element in elements { - self.append(element) +// MARK: - Collection +extension LinkedList: Collection { + + public typealias Index = LinkedListIndex + + /// The position of the first element in a nonempty collection. + /// + /// If the collection is empty, `startIndex` is equal to `endIndex`. + /// - Complexity: O(1) + public var startIndex: Index { + get { + return LinkedListIndex(node: head, tag: 0) + } + } + + /// The collection's "past the end" position---that is, the position one + /// greater than the last valid subscript argument. + /// - Complexity: O(n), where n is the number of elements in the list. This can be improved by keeping a reference + /// to the last node in the collection. + public var endIndex: Index { + get { + if let h = self.head { + return LinkedListIndex(node: h, tag: count) + } else { + return LinkedListIndex(node: nil, tag: startIndex.tag) + } + } + } + + public subscript(position: Index) -> T { + get { + return position.node!.value + } + } + + public func index(after idx: Index) -> Index { + return LinkedListIndex(node: idx.node?.next, tag: idx.tag + 1) } - } } +// MARK: - Collection Index +/// Custom index type that contains a reference to the node at index 'tag' +public struct LinkedListIndex: Comparable { + fileprivate let node: LinkedList.LinkedListNode? + fileprivate let tag: Int + + public static func==(lhs: LinkedListIndex, rhs: LinkedListIndex) -> Bool { + return (lhs.tag == rhs.tag) + } + + public static func< (lhs: LinkedListIndex, rhs: LinkedListIndex) -> Bool { + return (lhs.tag < rhs.tag) + } +} //: Ok, now that the declarations are done, let's see our Linked List in action: let list = LinkedList() list.isEmpty // true -list.first // nil +list.head // nil list.last // nil list.append("Hello") -list.isEmpty -list.first!.value // "Hello" +list.isEmpty // false +list.head!.value // "Hello" list.last!.value // "Hello" list.count // 1 list.append("World") -list.first!.value // "Hello" +list.head!.value // "Hello" list.last!.value // "World" list.count // 2 -list.first!.previous // nil -list.first!.next!.value // "World" +list.head!.previous // nil +list.head!.next!.value // "World" list.last!.previous!.value // "Hello" list.last!.next // nil -list.node(atIndex: 0)!.value // "Hello" -list.node(atIndex: 1)!.value // "World" -list.node(atIndex: 2) // nil +list.node(at: 0).value // "Hello" +list.node(at: 1).value // "World" +//list.node(at: 2) // crash! list[0] // "Hello" list[1] // "World" @@ -354,9 +381,9 @@ list.append(list2) // [Hello, World, Goodbye, World] list2.removeAll() // [ ] list2.isEmpty // true list.removeLast() // "World" -list.remove(atIndex: 2) // "Goodbye" +list.remove(at: 2) // "Goodbye" -list.insert("Swift", atIndex: 1) +list.insert("Swift", at: 1) list[0] // "Hello" list[1] // "Swift" list[2] // "World" @@ -364,25 +391,49 @@ print(list) list.reverse() // [World, Swift, Hello] -list.node(atIndex: 0)!.value = "Universe" -list.node(atIndex: 1)!.value = "Swifty" -let m = list.map { s in s.characters.count } +list.node(at: 0).value = "Universe" +list.node(at: 1).value = "Swifty" +let m = list.map { s in s.count } m // [8, 6, 5] -let f = list.filter { s in s.characters.count > 5 } +let f = list.filter { s in s.count > 5 } f // [Universe, Swifty] -list.remove(node: list.first!) // "Universe" +list.remove(node: list.head!) // "Universe" list.count // 2 list[0] // "Swifty" list[1] // "Hello" +list.count // 2 list.removeLast() // "Hello" +list.head?.value list.count // 1 list[0] // "Swifty" -list.remove(atIndex: 0) // "Swifty" +list.remove(at: 0) // "Swifty" list.count // 0 +let list3 = LinkedList() +list3.insert("2", at: 0) // [2] +list3.count // 1 +list3.insert("4", at: 1) // [2,4] +list3.count // 2 +list3.insert("5", at: 2) // [2,4,5] +list3.count // 3 +list3.insert("3", at: 1) // [2,3,4,5] +list3.insert("1", at: 0) // [1,2,3,4,5] + +let list4 = LinkedList() +list4.insert(list3, at: 0) // [1,2,3,4,5] +list4.count // 5 + +let list5 = LinkedList() +list5.append("0") // [0] +list5.insert("End", at:1) // [0,End] +list5.count // 2 +list5.insert(list4, at: 1) // [0,1,2,3,4,5,End] +list5.count // 7 + + let linkedList: LinkedList = [1, 2, 3, 4] // [1, 2, 3, 4] linkedList.count // 4 linkedList[0] // 1 @@ -392,3 +443,23 @@ let listArrayLiteral2: LinkedList = ["Swift", "Algorithm", "Club"] listArrayLiteral2.count // 3 listArrayLiteral2[0] // "Swift" listArrayLiteral2.removeLast() // "Club" + + +// Conformance to the Collection protocol +let collection: LinkedList = [1, 2, 3, 4, 5] +let index2 = collection.index(collection.startIndex, offsetBy: 2) +let value = collection[index2] // 3 + +// Iterating in a for loop, since the Sequence protocol allows this. +var sum = 0 +for element in collection { + sum += element +} +sum //15 + +// Another way of achieving the same result though 'reduce', another method defined in an extension of Sequence. Collections are Sequences. +let result = collection.reduce(0) {$0 + $1} // 15 + + + + diff --git a/Linked List/LinkedList.playground/contents.xcplayground b/Linked List/LinkedList.playground/contents.xcplayground index 3de2b51ba..5e28f2dd0 100644 --- a/Linked List/LinkedList.playground/contents.xcplayground +++ b/Linked List/LinkedList.playground/contents.xcplayground @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/Linked List/LinkedList.swift b/Linked List/LinkedList.swift index a5594eb31..de8a42c93 100755 --- a/Linked List/LinkedList.swift +++ b/Linked List/LinkedList.swift @@ -1,79 +1,103 @@ public final class LinkedList { - + + /// Linked List's Node Class Declaration public class LinkedListNode { var value: T var next: LinkedListNode? weak var previous: LinkedListNode? - + public init(value: T) { self.value = value } } - + + /// Typealiasing the node class to increase readability of code public typealias Node = LinkedListNode - - fileprivate var head: Node? - - public init() {} - - public var isEmpty: Bool { - return head == nil - } - - public var first: Node? { - return head - } - + + + /// The head of the Linked List + private(set) var head: Node? + + /// Computed property to iterate through the linked list and return the last node in the list (if any) public var last: Node? { - if var node = head { - while let next = node.next { - node = next - } - return node - } else { + guard var node = head else { return nil } + + while let next = node.next { + node = next + } + return node } - + + /// Computed property to check if the linked list is empty + public var isEmpty: Bool { + return head == nil + } + + /// Computed property to iterate through the linked list and return the total number of nodes public var count: Int { - if var node = head { - var c = 1 - while let next = node.next { - node = next - c += 1 - } - return c - } else { + guard var node = head else { return 0 } - } - - public func node(atIndex index: Int) -> Node? { - if index >= 0 { - var node = head - var i = index - while node != nil { - if i == 0 { return node } - i -= 1 - node = node!.next - } + + var count = 1 + while let next = node.next { + node = next + count += 1 } - return nil + return count } - + + /// Default initializer + public init() {} + + + /// Subscript function to return the node at a specific index + /// + /// - Parameter index: Integer value of the requested value's index public subscript(index: Int) -> T { - let node = self.node(atIndex: index) - assert(node != nil) - return node!.value + let node = self.node(at: index) + return node.value } - + + /// Function to return the node at a specific index. Crashes if index is out of bounds (0...self.count) + /// + /// - Parameter index: Integer value of the node's index to be returned + /// - Returns: LinkedListNode + public func node(at index: Int) -> Node { + assert(head != nil, "List is empty") + assert(index >= 0, "index must be greater or equal to 0") + + if index == 0 { + return head! + } else { + var node = head!.next + for _ in 1.. { head = newNode } } - + + /// Append a copy of a LinkedList to the end of the list. + /// + /// - Parameter list: The list to be copied and appended. public func append(_ list: LinkedList) { var nodeToCopy = list.head while let node = nodeToCopy { - self.append(node.value) + append(node.value) nodeToCopy = node.next } } - - private func nodesBeforeAndAfter(index: Int) -> (Node?, Node?) { - assert(index >= 0) - - var i = index - var next = head - var prev: Node? - - while next != nil && i > 0 { - i -= 1 - prev = next - next = next!.next - } - assert(i == 0) // if > 0, then specified index was too large - - return (prev, next) - } - - public func insert(_ value: T, atIndex index: Int) { + + /// Insert a value at a specific index. Crashes if index is out of bounds (0...self.count) + /// + /// - Parameters: + /// - value: The data value to be inserted + /// - index: Integer value of the index to be insterted at + public func insert(_ value: T, at index: Int) { let newNode = Node(value: value) - self.insert(newNode, atIndex: index) + insert(newNode, at: index) } - - public func insert(_ node: Node, atIndex index: Int) { - let (prev, next) = nodesBeforeAndAfter(index: index) - let newNode = LinkedListNode(value: node.value) - newNode.previous = prev - newNode.next = next - prev?.next = newNode - next?.previous = newNode - - if prev == nil { + + /// Insert a copy of a node at a specific index. Crashes if index is out of bounds (0...self.count) + /// + /// - Parameters: + /// - node: The node containing the value to be inserted + /// - index: Integer value of the index to be inserted at + public func insert(_ newNode: Node, at index: Int) { + if index == 0 { + newNode.next = head + head?.previous = newNode head = newNode + } else { + let prev = node(at: index - 1) + let next = prev.next + newNode.previous = prev + newNode.next = next + next?.previous = newNode + prev.next = newNode } } - - public func insert(_ list: LinkedList, atIndex index: Int) { - if list.isEmpty { return } - var (prev, next) = nodesBeforeAndAfter(index: index) - var nodeToCopy = list.head - var newNode: Node? - while let node = nodeToCopy { - newNode = Node(value: node.value) - newNode?.previous = prev - if let previous = prev { - previous.next = newNode - } else { - self.head = newNode - } - nodeToCopy = nodeToCopy?.next - prev = newNode + + /// Insert a copy of a LinkedList at a specific index. Crashes if index is out of bounds (0...self.count) + /// + /// - Parameters: + /// - list: The LinkedList to be copied and inserted + /// - index: Integer value of the index to be inserted at + public func insert(_ list: LinkedList, at index: Int) { + guard !list.isEmpty else { return } + + if index == 0 { + list.last?.next = head + head = list.head + } else { + let prev = node(at: index - 1) + let next = prev.next + + prev.next = list.head + list.head?.previous = prev + + list.last?.next = next + next?.previous = list.last } - prev?.next = next - next?.previous = prev } - + + /// Function to remove all nodes/value from the list public func removeAll() { head = nil } - + + /// Function to remove a specific node. + /// + /// - Parameter node: The node to be deleted + /// - Returns: The data value contained in the deleted node. @discardableResult public func remove(node: Node) -> T { let prev = node.previous let next = node.next - + if let prev = prev { prev.next = next } else { head = next } next?.previous = prev - + node.previous = nil node.next = nil return node.value } - + + /// Function to remove the last node/value in the list. Crashes if the list is empty + /// + /// - Returns: The data value contained in the deleted node. @discardableResult public func removeLast() -> T { assert(!isEmpty) return remove(node: last!) } - - @discardableResult public func remove(atIndex index: Int) -> T { - let node = self.node(atIndex: index) - assert(node != nil) - return remove(node: node!) + + /// Function to remove a node/value at a specific index. Crashes if index is out of bounds (0...self.count) + /// + /// - Parameter index: Integer value of the index of the node to be removed + /// - Returns: The data value contained in the deleted node + @discardableResult public func remove(at index: Int) -> T { + let node = self.node(at: index) + return remove(node: node) } } +//: End of the base class declarations & beginning of extensions' declarations: + +// MARK: - Extension to enable the standard conversion of a list to String extension LinkedList: CustomStringConvertible { public var description: String { var s = "[" var node = head - while node != nil { - s += "\(node!.value)" - node = node!.next + while let nd = node { + s += "\(nd.value)" + node = nd.next if node != nil { s += ", " } } return s + "]" } } +// MARK: - Extension to add a 'reverse' function to the list extension LinkedList { public func reverse() { var node = head @@ -201,46 +241,101 @@ extension LinkedList { } } +// MARK: - An extension with an implementation of 'map' & 'filter' functions extension LinkedList { public func map(transform: (T) -> U) -> LinkedList { let result = LinkedList() var node = head - while node != nil { - result.append(transform(node!.value)) - node = node!.next + while let nd = node { + result.append(transform(nd.value)) + node = nd.next } return result } - + public func filter(predicate: (T) -> Bool) -> LinkedList { let result = LinkedList() var node = head - while node != nil { - if predicate(node!.value) { - result.append(node!.value) + while let nd = node { + if predicate(nd.value) { + result.append(nd.value) } - node = node!.next + node = nd.next } return result } } +// MARK: - Extension to enable initialization from an Array extension LinkedList { convenience init(array: Array) { self.init() - - for element in array { - self.append(element) - } + + array.forEach { append($0) } } } +// MARK: - Extension to enable initialization from an Array Literal extension LinkedList: ExpressibleByArrayLiteral { - public convenience init(arrayLiteral elements: T...) { - self.init() + public convenience init(arrayLiteral elements: T...) { + self.init() + + elements.forEach { append($0) } + } +} - for element in elements { - self.append(element) +// MARK: - Collection +extension LinkedList: Collection { + + public typealias Index = LinkedListIndex + + /// The position of the first element in a nonempty collection. + /// + /// If the collection is empty, `startIndex` is equal to `endIndex`. + /// - Complexity: O(1) + public var startIndex: Index { + get { + return LinkedListIndex(node: head, tag: 0) + } + } + + /// The collection's "past the end" position---that is, the position one + /// greater than the last valid subscript argument. + /// - Complexity: O(n), where n is the number of elements in the list. This can be improved by keeping a reference + /// to the last node in the collection. + public var endIndex: Index { + get { + if let h = self.head { + return LinkedListIndex(node: h, tag: count) + } else { + return LinkedListIndex(node: nil, tag: startIndex.tag) + } + } + } + + public subscript(position: Index) -> T { + get { + return position.node!.value + } + } + + public func index(after idx: Index) -> Index { + return LinkedListIndex(node: idx.node?.next, tag: idx.tag + 1) } - } } + +// MARK: - Collection Index +/// Custom index type that contains a reference to the node at index 'tag' +public struct LinkedListIndex: Comparable { + fileprivate let node: LinkedList.LinkedListNode? + fileprivate let tag: Int + + public static func==(lhs: LinkedListIndex, rhs: LinkedListIndex) -> Bool { + return (lhs.tag == rhs.tag) + } + + public static func< (lhs: LinkedListIndex, rhs: LinkedListIndex) -> Bool { + return (lhs.tag < rhs.tag) + } +} + diff --git a/Linked List/README.markdown b/Linked List/README.markdown index 2e1f03ece..e81e00b9f 100644 --- a/Linked List/README.markdown +++ b/Linked List/README.markdown @@ -110,14 +110,14 @@ Let's also add a property that gives you the last node in the list. This is wher ```swift public var last: Node? { - if var node = head { - while let next = node.next { - node = next - } - return node - } else { + guard var node = head else { return nil } + + while let next = node.next { + node = next + } + return node } ``` @@ -198,16 +198,16 @@ Let's add a method to count how many nodes are in the list. This will look very ```swift public var count: Int { - if var node = head { - var c = 1 - while let next = node.next { - node = next - c += 1 - } - return c - } else { + guard var node = head else { return 0 } + + var count = 1 + while let next = node.next { + node = next + count += 1 + } + return count } ``` @@ -218,37 +218,43 @@ It loops through the list in the same manner but this time increments a counter What if we wanted to find the node at a specific index in the list? With an array we can just write `array[index]` and it's an **O(1)** operation. It's a bit more involved with linked lists, but again the code follows a similar pattern: ```swift - public func nodeAt(_ index: Int) -> Node? { - if index >= 0 { - var node = head - var i = index - while node != nil { - if i == 0 { return node } - i -= 1 - node = node!.next + public func node(atIndex index: Int) -> Node { + if index == 0 { + return head! + } else { + var node = head!.next + for _ in 1.. T { - let node = nodeAt(index) - assert(node != nil) - return node!.value + let node = node(atIndex: index) + return node.value } ``` @@ -264,84 +270,100 @@ It crashes on `list[2]` because there is no node at that index. So far we've written code to add new nodes to the end of the list, but that's slow because you need to find the end of the list first. (It would be fast if we used a tail pointer.) For this reason, if the order of the items in the list doesn't matter, you should insert at the front of the list instead. That's always an **O(1)** operation. -Let's write a method that lets you insert a new node at any index in the list. First, we'll define a helper function: - -```swift - private func nodesBeforeAndAfter(index: Int) -> (Node?, Node?) { - assert(index >= 0) - - var i = index - var next = head - var prev: Node? - while next != nil && i > 0 { - i -= 1 - prev = next - next = next!.next - } - assert(i == 0) - - return (prev, next) - } -``` - -This returns a tuple containing the node currently at the specified index and the node that immediately precedes it, if any. The loop is very similar to `nodeAtIndex()`, except that here we also keep track of what the previous node is as we iterate through the list. - -Let's look at an example. Suppose we have the following list: - - head --> A --> B --> C --> D --> E --> nil - -We want to find the nodes before and after index 3. As we start the loop, `i = 3`, `next` points at `"A"`, and `prev` is nil. - - head --> A --> B --> C --> D --> E --> nil - next - -We decrement `i`, make `prev` point to `"A"`, and move `next` to the next node, `"B"`: - - head --> A --> B --> C --> D --> E --> F --> nil - prev next - -Again, we decrement `i` and update the pointers. Now `prev` points to `"B"`, and `next` points to `"C"`: - - head --> A --> B --> C --> D --> E --> F --> nil - prev next - -As you can see, `prev` always follows one behind `next`. We do this one more time and then `i` equals 0 and we exit the loop: - - head --> A --> B --> C --> D --> E --> F --> nil - prev next - -The `assert()` after the loop checks whether there really were enough nodes in the list. If `i > 0` at this point, then the specified index was too large. - -> **Note:** If any of the loops in this article don't make much sense to you, then draw a linked list on a piece of paper and step through the loop by hand, just like what we did here. - -For this example, the function returns `("C", "D")` because `"D"` is the node at index 3 and `"C"` is the one right before that. - -Now that we have this helper function, we can write the method for inserting nodes: +Let's write a method that lets you insert a new node at any index in the list. ```swift - public func insert(value: T, atIndex index: Int) { - let (prev, next) = nodesBeforeAndAfter(index) // 1 - - let newNode = Node(value: value) // 2 - newNode.previous = prev - newNode.next = next - prev?.next = newNode - next?.previous = newNode - - if prev == nil { // 3 - head = newNode - } - } + public func insert(_ node: Node, atIndex index: Int) { + let newNode = node + if index == 0 { + newNode.next = head + head?.previous = newNode + head = newNode + } else { + let prev = self.node(atIndex: index-1) + let next = prev.next + + newNode.previous = prev + newNode.next = prev.next + prev.next = newNode + next?.previous = newNode + } +} ``` -Some remarks about this method: - -1. First, we need to find where to insert this node. After calling the helper method, `prev` points to the previous node and `next` is the node currently at the given index. We'll insert the new node in between these two. Note that `prev` can be nil (index is 0), `next` can be nil (index equals size of the list), or both can be nil if the list is empty. +As with node(atIndex :) method, insert(_: at:) method also branches depending on whether the given index is 0 or not. +First let's look at the former case. Suppose we have the following list and the new node(C). + + +---------+ +---------+ + head --->| |---->| |-----//-----> + | A | | B | + nil <---| |<----| |<----//------ + +---------+ +---------+ + [0] [1] + + + +---------+ + new --->| |----> nil + | C | + | | + +---------+ + +Now put the new node before the first node. In this way: + + new.next = head + head.previous = new + + +---------+ +---------+ +---------+ + new --->| |--> head -->| |---->| |-----//-----> + | C | | A | | B | + | |<-----------| |<----| |<----//------ + +---------+ +---------+ +---------+ + + +Finally, replace the head with the new node. + + head = new + + +---------+ +---------+ +---------+ + head --->| |--->| |---->| |-----//-----> + | C | | A | | B | + nil <---| |<---| |<----| |<----//------ + +---------+ +---------+ +---------+ + [0] [1] [2] + + +However, when the given index is greater than 0, it is necessary to get the node previous and next index and insert between them. +You can also obtain the previous and next node using node(atIndex:) as follows: + + +---------+ +---------+ +---------+ + head --->| |---//--->| |---->| |---- + | | | A | | B | + nil <---| |---//<---| |<----| |<--- + +---------+ +---------+ +---------+ + [0] [index-1] [index] + ^ ^ + | | + prev next + + prev = node(at: index-1) + next = prev.next + +Now insert new node between the prev and the next. + + new.prev = prev; prev.next = new // connect prev and new. + new.next = next; next.prev = new // connect new and next. + + +---------+ +---------+ +---------+ +---------+ + head --->| |---//--->| |---->| |---->| | + | | | A | | C | | B | + nil <---| |---//<---| |<----| |<----| | + +---------+ +---------+ +---------+ +---------+ + [0] [index-1] [index] [index+1] + ^ ^ ^ + | | | + prev new next -2. Create the new node and connect the `previous` and `next` pointers. Because the local `prev` and `next` variables are optionals and may be nil, so we use optional chaining here. - -3. If the new node is being inserted at the front of the list, we need to update the `head` pointer. (Note: If the list had a tail pointer, you'd also need to update that pointer here if `next == nil`, because that means the last element has changed.) Try it out: @@ -353,8 +375,7 @@ list[2] // "World" ``` Also try adding new nodes to the front and back of the list, to verify that this works properly. - -> **Note:** The `nodesBeforeAndAfter()` and `insert(atIndex)` functions can also be used with a singly linked list because we don't depend on the node's `previous` pointer to find the previous element. +> **Note:** The `node(atIndex:)` and `insert(_: atIndex:)` functions can also be used with a singly linked list because we don't depend on the node's `previous` pointer to find the previous element. What else do we need? Removing nodes, of course! First we'll do `removeAll()`, which is really simple: @@ -515,7 +536,7 @@ And here's filter: And a silly example: ```swift -let f = list.filter { s in s.characters.count > 5 } +let f = list.filter { s in s.count > 5 } f // [Universe, Swifty] ``` @@ -534,10 +555,76 @@ enum ListNode { } ``` -The big difference with the class-based version is that any modification you make to this list will result in a *new copy* being created. Whether that's what you want or not depends on the application. +The big difference with the enum-based version is that any modification you make to this list will result in a *new copy* being created because of [Swift's value semantics](https://developer.apple.com/swift/blog/?id=10). Whether that's what you want or not depends on the application. [I might fill out this section in more detail if there's a demand for it.] +## Conforming to the Collection protocol +Types that conform to the Sequence protocol, whose elements can be traversed multiple times, nondestructively, and accessed by indexed subscript should conform to the Collection protocol defined in Swift's Standard Library. + +Doing so grants access to a very large number of properties and operations that are common when dealing collections of data. In addition to this, it lets custom types follow the patterns that are common to Swift developers. + +In order to conform to this protocol, classes need to provide: + 1 `startIndex` and `endIndex` properties. + 2 Subscript access to elements as O(1). Diversions of this time complexity need to be documented. + +```swift +/// The position of the first element in a nonempty collection. +public var startIndex: Index { + get { + return LinkedListIndex(node: head, tag: 0) + } +} + +/// The collection's "past the end" position---that is, the position one +/// greater than the last valid subscript argument. +/// - Complexity: O(n), where n is the number of elements in the list. +/// This diverts from the protocol's expectation. +public var endIndex: Index { + get { + if let h = self.head { + return LinkedListIndex(node: h, tag: count) + } else { + return LinkedListIndex(node: nil, tag: startIndex.tag) + } + } +} +``` + +```swift +public subscript(position: Index) -> T { + get { + return position.node!.value + } +} +``` + +Becuase collections are responsible for managing their own indexes, the implementation below keeps a reference to a node in the list. A tag property in the index represents the position of the node in the list. + +```swift +/// Custom index type that contains a reference to the node at index 'tag' +public struct LinkedListIndex : Comparable +{ + fileprivate let node: LinkedList.LinkedListNode? + fileprivate let tag: Int + + public static func==(lhs: LinkedListIndex, rhs: LinkedListIndex) -> Bool { + return (lhs.tag == rhs.tag) + } + + public static func< (lhs: LinkedListIndex, rhs: LinkedListIndex) -> Bool { + return (lhs.tag < rhs.tag) + } +} +``` + +Finally, the linked is is able to calculate the index after a given one with the following implementation. +```swift +public func index(after idx: Index) -> Index { + return LinkedListIndex(node: idx.node?.next, tag: idx.tag+1) +} +``` + ## Some things to keep in mind Linked lists are flexible but many operations are **O(n)**. diff --git a/Linked List/Tests/LinkedListTests.swift b/Linked List/Tests/LinkedListTests.swift index 0b2b116df..b16af05ba 100755 --- a/Linked List/Tests/LinkedListTests.swift +++ b/Linked List/Tests/LinkedListTests.swift @@ -1,340 +1,339 @@ import XCTest class LinkedListTest: XCTestCase { - let numbers = [8, 2, 10, 9, 7, 5] - - fileprivate func buildList() -> LinkedList { - let list = LinkedList() - for number in numbers { - list.append(number) + let numbers = [8, 2, 10, 9, 7, 5] + + fileprivate func buildList() -> LinkedList { + let list = LinkedList() + for number in numbers { + list.append(number) + } + return list } - return list - } - - func testSwift4() { - // last checked with Xcode 9.0b4 - #if swift(>=4.0) - print("Hello, Swift 4!") - #endif - } - - func testEmptyList() { - let list = LinkedList() - XCTAssertTrue(list.isEmpty) - XCTAssertEqual(list.count, 0) - XCTAssertNil(list.first) - XCTAssertNil(list.last) - } - - func testListWithOneElement() { - let list = LinkedList() - list.append(123) - - XCTAssertFalse(list.isEmpty) - XCTAssertEqual(list.count, 1) - - XCTAssertNotNil(list.first) - XCTAssertNil(list.first!.previous) - XCTAssertNil(list.first!.next) - XCTAssertEqual(list.first!.value, 123) - - XCTAssertNotNil(list.last) - XCTAssertNil(list.last!.previous) - XCTAssertNil(list.last!.next) - XCTAssertEqual(list.last!.value, 123) - - XCTAssertTrue(list.first === list.last) - } - - func testListWithTwoElements() { - let list = LinkedList() - list.append(123) - list.append(456) - - XCTAssertEqual(list.count, 2) - - XCTAssertNotNil(list.first) - XCTAssertEqual(list.first!.value, 123) - - XCTAssertNotNil(list.last) - XCTAssertEqual(list.last!.value, 456) - - XCTAssertTrue(list.first !== list.last) - - XCTAssertNil(list.first!.previous) - XCTAssertTrue(list.first!.next === list.last) - XCTAssertTrue(list.last!.previous === list.first) - XCTAssertNil(list.last!.next) - } - - func testListWithThreeElements() { - let list = LinkedList() - list.append(123) - list.append(456) - list.append(789) - - XCTAssertEqual(list.count, 3) - - XCTAssertNotNil(list.first) - XCTAssertEqual(list.first!.value, 123) - - let second = list.first!.next - XCTAssertNotNil(second) - XCTAssertEqual(second!.value, 456) - - XCTAssertNotNil(list.last) - XCTAssertEqual(list.last!.value, 789) - - XCTAssertNil(list.first!.previous) - XCTAssertTrue(list.first!.next === second) - XCTAssertTrue(second!.previous === list.first) - XCTAssertTrue(second!.next === list.last) - XCTAssertTrue(list.last!.previous === second) - XCTAssertNil(list.last!.next) - } - - func testNodeAtIndexInEmptyList() { - let list = LinkedList() - let node = list.node(atIndex: 0) - XCTAssertNil(node) - } - - func testNodeAtIndexInListWithOneElement() { - let list = LinkedList() - list.append(123) - - let node = list.node(atIndex: 0) - XCTAssertNotNil(node) - XCTAssertEqual(node!.value, 123) - XCTAssertTrue(node === list.first) - } - - func testNodeAtIndex() { - let list = buildList() - - let nodeCount = list.count - XCTAssertEqual(nodeCount, numbers.count) - - XCTAssertNil(list.node(atIndex: -1)) - XCTAssertNil(list.node(atIndex: nodeCount)) - - let first = list.node(atIndex: 0) - XCTAssertNotNil(first) - XCTAssertTrue(first === list.first) - XCTAssertEqual(first!.value, numbers[0]) - - let last = list.node(atIndex: nodeCount - 1) - XCTAssertNotNil(last) - XCTAssertTrue(last === list.last) - XCTAssertEqual(last!.value, numbers[nodeCount - 1]) - - for i in 0..=4.0) + print("Hello, Swift 4!") + #endif } - } - - func testSubscript() { - let list = buildList() - for i in 0 ..< list.count { - XCTAssertEqual(list[i], numbers[i]) + + func testEmptyList() { + let list = LinkedList() + XCTAssertTrue(list.isEmpty) + XCTAssertEqual(list.count, 0) + XCTAssertNil(list.first) + XCTAssertNil(list.last) + } + + func testListWithOneElement() { + let list = LinkedList() + list.append(123) + + XCTAssertFalse(list.isEmpty) + XCTAssertEqual(list.count, 1) + + XCTAssertNotNil(list.first) + XCTAssertNil(list.head!.previous) + XCTAssertNil(list.head!.next) + XCTAssertEqual(list.head!.value, 123) + + XCTAssertNotNil(list.last) + XCTAssertNil(list.last!.previous) + XCTAssertNil(list.last!.next) + XCTAssertEqual(list.last!.value, 123) + + XCTAssertTrue(list.head === list.last) + } + + func testListWithTwoElements() { + let list = LinkedList() + list.append(123) + list.append(456) + + XCTAssertEqual(list.count, 2) + + XCTAssertNotNil(list.first) + XCTAssertEqual(list.head!.value, 123) + + XCTAssertNotNil(list.last) + XCTAssertEqual(list.last!.value, 456) + + XCTAssertTrue(list.head !== list.last) + + XCTAssertNil(list.head!.previous) + XCTAssertTrue(list.head!.next === list.last) + XCTAssertTrue(list.last!.previous === list.head) + XCTAssertNil(list.last!.next) + } + + func testListWithThreeElements() { + let list = LinkedList() + list.append(123) + list.append(456) + list.append(789) + + XCTAssertEqual(list.count, 3) + + XCTAssertNotNil(list.first) + XCTAssertEqual(list.head!.value, 123) + + let second = list.head!.next + XCTAssertNotNil(second) + XCTAssertEqual(second!.value, 456) + + XCTAssertNotNil(list.last) + XCTAssertEqual(list.last!.value, 789) + + XCTAssertNil(list.head!.previous) + XCTAssertTrue(list.head!.next === second) + XCTAssertTrue(second!.previous === list.head) + XCTAssertTrue(second!.next === list.last) + XCTAssertTrue(list.last!.previous === second) + XCTAssertNil(list.last!.next) + } + + func testNodeAtIndexInListWithOneElement() { + let list = LinkedList() + list.append(123) + + let node = list.node(at: 0) + XCTAssertNotNil(node) + XCTAssertEqual(node.value, 123) + XCTAssertTrue(node === list.head) + } + + func testNodeAtIndex() { + let list = buildList() + + let nodeCount = list.count + XCTAssertEqual(nodeCount, numbers.count) + + let first = list.node(at: 0) + XCTAssertNotNil(first) + XCTAssertTrue(first === list.head) + XCTAssertEqual(first.value, numbers[0]) + + let last = list.node(at: nodeCount - 1) + XCTAssertNotNil(last) + XCTAssertTrue(last === list.last) + XCTAssertEqual(last.value, numbers[nodeCount - 1]) + + for i in 0..() + list.insert(123, at: 0) + + XCTAssertFalse(list.isEmpty) + XCTAssertEqual(list.count, 1) + + let node = list.node(at: 0) + XCTAssertNotNil(node) + XCTAssertEqual(node.value, 123) + } + + func testInsertAtIndex() { + let list = buildList() + let prev = list.node(at: 2) + let next = list.node(at: 3) + let nodeCount = list.count + + list.insert(444, at: 3) + + let node = list.node(at: 3) + XCTAssertNotNil(node) + XCTAssertEqual(node.value, 444) + XCTAssertEqual(nodeCount + 1, list.count) + + XCTAssertFalse(prev === node) + XCTAssertFalse(next === node) + XCTAssertTrue(prev.next === node) + XCTAssertTrue(next.previous === node) + } + + func testInsertListAtIndex() { + let list = buildList() + let list2 = LinkedList() + list2.append(99) + list2.append(102) + list.insert(list2, at: 2) + XCTAssertTrue(list.count == 8) + XCTAssertEqual(list.node(at: 1).value, 2) + XCTAssertEqual(list.node(at: 2).value, 99) + XCTAssertEqual(list.node(at: 3).value, 102) + XCTAssertEqual(list.node(at: 4).value, 10) + } + + func testInsertListAtFirstIndex() { + let list = buildList() + let list2 = LinkedList() + list2.append(99) + list2.append(102) + list.insert(list2, at: 0) + XCTAssertTrue(list.count == 8) + XCTAssertEqual(list.node(at: 0).value, 99) + XCTAssertEqual(list.node(at: 1).value, 102) + XCTAssertEqual(list.node(at: 2).value, 8) + } + + func testInsertListAtLastIndex() { + let list = buildList() + let list2 = LinkedList() + list2.append(99) + list2.append(102) + list.insert(list2, at: list.count) + XCTAssertTrue(list.count == 8) + XCTAssertEqual(list.node(at: 5).value, 5) + XCTAssertEqual(list.node(at: 6).value, 99) + XCTAssertEqual(list.node(at: 7).value, 102) + } + + func testAppendList() { + let list = buildList() + let list2 = LinkedList() + list2.append(99) + list2.append(102) + list.append(list2) + XCTAssertTrue(list.count == 8) + XCTAssertEqual(list.node(at: 5).value, 5) + XCTAssertEqual(list.node(at: 6).value, 99) + XCTAssertEqual(list.node(at: 7).value, 102) + } + + func testAppendListToEmptyList() { + let list = LinkedList() + let list2 = LinkedList() + list2.append(5) + list2.append(10) + list.append(list2) + XCTAssertTrue(list.count == 2) + XCTAssertEqual(list.node(at: 0).value, 5) + XCTAssertEqual(list.node(at: 1).value, 10) + } + + func testRemoveAtIndexOnListWithOneElement() { + let list = LinkedList() + list.append(123) + + let value = list.remove(at: 0) + XCTAssertEqual(value, 123) + + XCTAssertTrue(list.isEmpty) + XCTAssertEqual(list.count, 0) + XCTAssertNil(list.first) + XCTAssertNil(list.last) + } + + func testRemoveAtIndex() { + let list = buildList() + let prev = list.node(at: 2) + let next = list.node(at: 3) + let nodeCount = list.count + + list.insert(444, at: 3) + + let value = list.remove(at: 3) + XCTAssertEqual(value, 444) + + let node = list.node(at: 3) + XCTAssertTrue(next === node) + XCTAssertTrue(prev.next === node) + XCTAssertTrue(node.previous === prev) + XCTAssertEqual(nodeCount, list.count) + } + + func testRemoveLastOnListWithOneElement() { + let list = LinkedList() + list.append(123) + + let value = list.removeLast() + XCTAssertEqual(value, 123) + + XCTAssertTrue(list.isEmpty) + XCTAssertEqual(list.count, 0) + XCTAssertNil(list.first) + XCTAssertNil(list.last) + } + + func testRemoveLast() { + let list = buildList() + let last = list.last + let prev = last!.previous + let nodeCount = list.count + + let value = list.removeLast() + XCTAssertEqual(value, 5) + + XCTAssertNil(last!.previous) + XCTAssertNil(last!.next) + + XCTAssertNil(prev!.next) + XCTAssertTrue(list.last === prev) + XCTAssertEqual(nodeCount - 1, list.count) + } + + func testRemoveAll() { + let list = buildList() + list.removeAll() + XCTAssertTrue(list.isEmpty) + XCTAssertEqual(list.count, 0) + XCTAssertNil(list.first) + XCTAssertNil(list.last) + } + + func testReverseLinkedList() { + let list = buildList() + let first = list.head + let last = list.last + let nodeCount = list.count + + list.reverse() + + XCTAssertTrue(first === list.last) + XCTAssertTrue(last === list.head) + XCTAssertEqual(nodeCount, list.count) + } + + func testArrayLiteralInitTypeInfer() { + let arrayLiteralInitInfer: LinkedList = [1.0, 2.0, 3.0] + + XCTAssertEqual(arrayLiteralInitInfer.count, 3) + XCTAssertEqual(arrayLiteralInitInfer.head?.value, 1.0) + XCTAssertEqual(arrayLiteralInitInfer.last?.value, 3.0) + XCTAssertEqual(arrayLiteralInitInfer[1], 2.0) + XCTAssertEqual(arrayLiteralInitInfer.removeLast(), 3.0) + XCTAssertEqual(arrayLiteralInitInfer.count, 2) + } + + func testArrayLiteralInitExplicit() { + let arrayLiteralInitExplicit: LinkedList = [1, 2, 3] + + XCTAssertEqual(arrayLiteralInitExplicit.count, 3) + XCTAssertEqual(arrayLiteralInitExplicit.head?.value, 1) + XCTAssertEqual(arrayLiteralInitExplicit.last?.value, 3) + XCTAssertEqual(arrayLiteralInitExplicit[1], 2) + XCTAssertEqual(arrayLiteralInitExplicit.removeLast(), 3) + XCTAssertEqual(arrayLiteralInitExplicit.count, 2) + } + + func testConformanceToCollectionProtocol() { + let collection: LinkedList = [1, 2, 3, 4, 5] + let index2 = collection.index(collection.startIndex, offsetBy: 2) + let value = collection[index2] + + XCTAssertTrue(value == 3) } - } - - func testInsertAtIndexInEmptyList() { - let list = LinkedList() - list.insert(123, atIndex: 0) - - XCTAssertFalse(list.isEmpty) - XCTAssertEqual(list.count, 1) - - let node = list.node(atIndex: 0) - XCTAssertNotNil(node) - XCTAssertEqual(node!.value, 123) - } - - func testInsertAtIndex() { - let list = buildList() - let prev = list.node(atIndex: 2) - let next = list.node(atIndex: 3) - let nodeCount = list.count - - list.insert(444, atIndex: 3) - - let node = list.node(atIndex: 3) - XCTAssertNotNil(node) - XCTAssertEqual(node!.value, 444) - XCTAssertEqual(nodeCount + 1, list.count) - - XCTAssertFalse(prev === node) - XCTAssertFalse(next === node) - XCTAssertTrue(prev!.next === node) - XCTAssertTrue(next!.previous === node) - } - - func testInsertListAtIndex() { - let list = buildList() - let list2 = LinkedList() - list2.append(99) - list2.append(102) - list.insert(list2, atIndex: 2) - XCTAssertTrue(list.count == 8) - XCTAssertEqual(list.node(atIndex: 1)?.value, 2) - XCTAssertEqual(list.node(atIndex: 2)?.value, 99) - XCTAssertEqual(list.node(atIndex: 3)?.value, 102) - XCTAssertEqual(list.node(atIndex: 4)?.value, 10) - } - - func testInsertListAtFirstIndex() { - let list = buildList() - let list2 = LinkedList() - list2.append(99) - list2.append(102) - list.insert(list2, atIndex: 0) - XCTAssertTrue(list.count == 8) - XCTAssertEqual(list.node(atIndex: 0)?.value, 99) - XCTAssertEqual(list.node(atIndex: 1)?.value, 102) - XCTAssertEqual(list.node(atIndex: 2)?.value, 8) - } - - func testInsertListAtLastIndex() { - let list = buildList() - let list2 = LinkedList() - list2.append(99) - list2.append(102) - list.insert(list2, atIndex: list.count) - XCTAssertTrue(list.count == 8) - XCTAssertEqual(list.node(atIndex: 5)?.value, 5) - XCTAssertEqual(list.node(atIndex: 6)?.value, 99) - XCTAssertEqual(list.node(atIndex: 7)?.value, 102) - } - - func testAppendList() { - let list = buildList() - let list2 = LinkedList() - list2.append(99) - list2.append(102) - list.append(list2) - XCTAssertTrue(list.count == 8) - XCTAssertEqual(list.node(atIndex: 5)?.value, 5) - XCTAssertEqual(list.node(atIndex: 6)?.value, 99) - XCTAssertEqual(list.node(atIndex: 7)?.value, 102) - } - - func testAppendListToEmptyList() { - let list = LinkedList() - let list2 = LinkedList() - list2.append(5) - list2.append(10) - list.append(list2) - XCTAssertTrue(list.count == 2) - XCTAssertEqual(list.node(atIndex: 0)?.value, 5) - XCTAssertEqual(list.node(atIndex: 1)?.value, 10) - } - - func testRemoveAtIndexOnListWithOneElement() { - let list = LinkedList() - list.append(123) - - let value = list.remove(atIndex: 0) - XCTAssertEqual(value, 123) - - XCTAssertTrue(list.isEmpty) - XCTAssertEqual(list.count, 0) - XCTAssertNil(list.first) - XCTAssertNil(list.last) - } - - func testRemoveAtIndex() { - let list = buildList() - let prev = list.node(atIndex: 2) - let next = list.node(atIndex: 3) - let nodeCount = list.count - - list.insert(444, atIndex: 3) - - let value = list.remove(atIndex: 3) - XCTAssertEqual(value, 444) - - let node = list.node(atIndex: 3) - XCTAssertTrue(next === node) - XCTAssertTrue(prev!.next === node) - XCTAssertTrue(node!.previous === prev) - XCTAssertEqual(nodeCount, list.count) - } - - func testRemoveLastOnListWithOneElement() { - let list = LinkedList() - list.append(123) - - let value = list.removeLast() - XCTAssertEqual(value, 123) - - XCTAssertTrue(list.isEmpty) - XCTAssertEqual(list.count, 0) - XCTAssertNil(list.first) - XCTAssertNil(list.last) - } - - func testRemoveLast() { - let list = buildList() - let last = list.last - let prev = last!.previous - let nodeCount = list.count - - let value = list.removeLast() - XCTAssertEqual(value, 5) - - XCTAssertNil(last!.previous) - XCTAssertNil(last!.next) - - XCTAssertNil(prev!.next) - XCTAssertTrue(list.last === prev) - XCTAssertEqual(nodeCount - 1, list.count) - } - - func testRemoveAll() { - let list = buildList() - list.removeAll() - XCTAssertTrue(list.isEmpty) - XCTAssertEqual(list.count, 0) - XCTAssertNil(list.first) - XCTAssertNil(list.last) - } - - func testReverseLinkedList() { - let list = buildList() - let first = list.first - let last = list.last - let nodeCount = list.count - - list.reverse() - - XCTAssertTrue(first === list.last) - XCTAssertTrue(last === list.first) - XCTAssertEqual(nodeCount, list.count) - } - - func testArrayLiteralInitTypeInfer() { - let arrayLiteralInitInfer: LinkedList = [1.0, 2.0, 3.0] - - XCTAssertEqual(arrayLiteralInitInfer.count, 3) - XCTAssertEqual(arrayLiteralInitInfer.first?.value, 1.0) - XCTAssertEqual(arrayLiteralInitInfer.last?.value, 3.0) - XCTAssertEqual(arrayLiteralInitInfer[1], 2.0) - XCTAssertEqual(arrayLiteralInitInfer.removeLast(), 3.0) - XCTAssertEqual(arrayLiteralInitInfer.count, 2) - } - - func testArrayLiteralInitExplicit() { - let arrayLiteralInitExplicit: LinkedList = [1, 2, 3] - - XCTAssertEqual(arrayLiteralInitExplicit.count, 3) - XCTAssertEqual(arrayLiteralInitExplicit.first?.value, 1) - XCTAssertEqual(arrayLiteralInitExplicit.last?.value, 3) - XCTAssertEqual(arrayLiteralInitExplicit[1], 2) - XCTAssertEqual(arrayLiteralInitExplicit.removeLast(), 3) - XCTAssertEqual(arrayLiteralInitExplicit.count, 2) - } } diff --git a/Longest Common Subsequence/README.markdown b/Longest Common Subsequence/README.markdown index 4cd2c2fcb..8a4a763c6 100644 --- a/Longest Common Subsequence/README.markdown +++ b/Longest Common Subsequence/README.markdown @@ -99,7 +99,7 @@ And so on... this is how `lcsLength(_:)` fills in the entire matrix. ## Backtracking to find the actual subsequence -So far we've calculated the length of every possible subsequence. The length of the longest subsequence is found in the bottom-left corner of matrix, at `matrix[n+1][m+1]`. In the above example it is 4, so the LCS consists of 4 characters. +So far we've calculated the length of every possible subsequence. The length of the longest subsequence is found in the bottom-right corner of matrix, at `matrix[n+1][m+1]`. In the above example it is 4, so the LCS consists of 4 characters. Having the length of every combination of substrings makes it possible to determine *which* characters are part of the LCS itself by using a backtracking strategy. @@ -156,7 +156,7 @@ func backtrack(_ matrix: [[Int]]) -> String { } ``` -This backtracks from `matrix[n+1][m+1]` (bottom-right corner) to `matrix[1][1]` (top-right corner), looking for characters that are common to both strings. It adds those characters to a new string, `lcs`. +This backtracks from `matrix[n+1][m+1]` (bottom-right corner) to `matrix[1][1]` (top-left corner), looking for characters that are common to both strings. It adds those characters to a new string, `lcs`. The `charInSequence` variable is an index into the string given by `self`. Initially this points to the last character of the string. Each time we decrement `i`, we also move back `charInSequence`. When the two characters are found to be equal, we add the character at `self[charInSequence]` to the new `lcs` string. (We can't just write `self[i]` because `i` may not map to the current position inside the Swift string.) diff --git a/Merge Sort/MergeSort.playground/Contents.swift b/Merge Sort/MergeSort.playground/Contents.swift index b05332bfe..ca1347890 100644 --- a/Merge Sort/MergeSort.playground/Contents.swift +++ b/Merge Sort/MergeSort.playground/Contents.swift @@ -1,10 +1,5 @@ /* Top-down recursive version */ -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - func mergeSort(_ array: [T]) -> [T] { guard array.count > 1 else { return array } let middleIndex = array.count / 2 diff --git a/Merge Sort/MergeSort.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Merge Sort/MergeSort.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Merge Sort/MergeSort.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Merge Sort/README.markdown b/Merge Sort/README.markdown index 7ad6fd95e..d86f062f4 100644 --- a/Merge Sort/README.markdown +++ b/Merge Sort/README.markdown @@ -79,6 +79,7 @@ func merge(leftPile: [Int], rightPile: [Int]) -> [Int] { // 2 var orderedPile = [Int]() + orderedPile.reserveCapacity(leftPile.count + rightPile.count) // 3 while leftIndex < leftPile.count && rightIndex < rightPile.count { @@ -115,7 +116,7 @@ This method may look scary, but it is quite straightforward: 1. You need two indexes to keep track of your progress for the two arrays while merging. -2. This is the merged array. It is empty right now, but you will build it up in subsequent steps by appending elements from the other arrays. +2. This is the merged array. It is empty right now, but you will build it up in subsequent steps by appending elements from the other arrays. Since you already know number of elements that will end up in this array, you reserve capacity to avoid reallocation overhead later. 3. This while-loop will compare the elements from the left and right sides and append them into the `orderedPile` while making sure that the result stays in order. diff --git a/Miller-Rabin Primality Test/MRPrimality.playground/Contents.swift b/Miller-Rabin Primality Test/MRPrimality.playground/Contents.swift index d8fd7fad8..35b457d1c 100644 --- a/Miller-Rabin Primality Test/MRPrimality.playground/Contents.swift +++ b/Miller-Rabin Primality Test/MRPrimality.playground/Contents.swift @@ -1,29 +1,26 @@ //: Playground - noun: a place where people can play -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - -// Real primes -mrPrimalityTest(5) -mrPrimalityTest(439) -mrPrimalityTest(1201) -mrPrimalityTest(143477) -mrPrimalityTest(1299869) -mrPrimalityTest(15487361) -mrPrimalityTest(179426363) -mrPrimalityTest(32416187747) - -// Fake primes -mrPrimalityTest(15) -mrPrimalityTest(435) -mrPrimalityTest(1207) -mrPrimalityTest(143473) -mrPrimalityTest(1291869) -mrPrimalityTest(15487161) -mrPrimalityTest(178426363) -mrPrimalityTest(32415187747) - -// With iteration -mrPrimalityTest(32416190071, iteration: 10) +do { + // Real primes + try checkWithMillerRabin(5) + try checkWithMillerRabin(439) + try checkWithMillerRabin(1201) + try checkWithMillerRabin(143477) + try checkWithMillerRabin(1299869) + try checkWithMillerRabin(15487361) + try checkWithMillerRabin(179426363) + + // Fake primes + try checkWithMillerRabin(15) + try checkWithMillerRabin(435) + try checkWithMillerRabin(1207) + try checkWithMillerRabin(143473) + try checkWithMillerRabin(1291869) + try checkWithMillerRabin(15487161) + try checkWithMillerRabin(178426363) + + // Specifying accuracy + try checkWithMillerRabin(179426363, accuracy: 10) +} catch { + dump(error) +} \ No newline at end of file diff --git a/Miller-Rabin Primality Test/MRPrimality.playground/Sources/MRPrimality.swift b/Miller-Rabin Primality Test/MRPrimality.playground/Sources/MRPrimality.swift deleted file mode 100644 index ee2ace8b9..000000000 --- a/Miller-Rabin Primality Test/MRPrimality.playground/Sources/MRPrimality.swift +++ /dev/null @@ -1,111 +0,0 @@ -// -// MRPrimality.swift -// -// -// Created by Sahn Cha on 2016. 10. 18.. -// -// - -import Foundation - -/* - Miller-Rabin Primality Test. - One of the most used algorithms for primality testing. - Since this is a probabilistic algorithm, it needs to be executed multiple times to increase accuracy. - - Inputs: - n: UInt64 { target integer to be tested for primality } - k: Int { optional. number of iterations } - - Outputs: - true { probably prime } - false { composite } - */ -public func mrPrimalityTest(_ n: UInt64, iteration k: Int = 1) -> Bool { - guard n > 2 && n % 2 == 1 else { return false } - - var d = n - 1 - var s = 0 - - while d % 2 == 0 { - d /= 2 - s += 1 - } - - let range = UInt64.max - UInt64.max % (n - 2) - var r: UInt64 = 0 - repeat { - arc4random_buf(&r, MemoryLayout.size(ofValue: r)) - } while r >= range - - r = r % (n - 2) + 2 - - for _ in 1 ... k { - var x = powmod64(r, d, n) - if x == 1 || x == n - 1 { continue } - - if s == 1 { s = 2 } - - for _ in 1 ... s - 1 { - x = powmod64(x, 2, n) - if x == 1 { return false } - if x == n - 1 { break } - } - - if x != n - 1 { return false } - } - - return true -} - -/* - Calculates (base^exp) mod m. - - Inputs: - base: UInt64 { base } - exp: UInt64 { exponent } - m: UInt64 { modulus } - - Outputs: - the result - */ -private func powmod64(_ base: UInt64, _ exp: UInt64, _ m: UInt64) -> UInt64 { - if m == 1 { return 0 } - - var result: UInt64 = 1 - var b = base % m - var e = exp - - while e > 0 { - if e % 2 == 1 { result = mulmod64(result, b, m) } - b = mulmod64(b, b, m) - e >>= 1 - } - - return result -} - -/* - Calculates (first * second) mod m, hopefully without overflow. :] - - Inputs: - first: UInt64 { first integer } - second: UInt64 { second integer } - m: UInt64 { modulus } - - Outputs: - the result - */ -private func mulmod64(_ first: UInt64, _ second: UInt64, _ m: UInt64) -> UInt64 { - var result: UInt64 = 0 - var a = first - var b = second - - while a != 0 { - if a % 2 == 1 { result = ((result % m) + (b % m)) % m } // This may overflow if 'm' is a 64bit number && both 'result' and 'b' are very close to but smaller than 'm'. - a >>= 1 - b = (b << 1) % m - } - - return result -} diff --git a/Miller-Rabin Primality Test/MRPrimality.playground/Sources/MillerRabin.swift b/Miller-Rabin Primality Test/MRPrimality.playground/Sources/MillerRabin.swift new file mode 100644 index 000000000..03daac1fa --- /dev/null +++ b/Miller-Rabin Primality Test/MRPrimality.playground/Sources/MillerRabin.swift @@ -0,0 +1,100 @@ +// +// MRPrimality.swift +// +// +// Created by Sahn Cha on 2016. 10. 18.. +// +// + +import Foundation + +enum MillerRabinError: Error { + case primeLowAccuracy + case primeLowerBorder + case uIntOverflow +} + +/* + The Miller–Rabin test relies on an equality or set of equalities that + hold true for prime values, then checks whether or not they hold for + a number that we want to test for primality. + + - Parameter n: an odd integer to be tested for primality; + - Parameter k: a parameter that determines the accuracy of the test + - throws: Can throw an error of type `MillerRabinError`. + - Returns: composite if n is composite, otherwise probably prime + */ +public func checkWithMillerRabin(_ n: UInt, accuracy k: UInt = 1) throws -> Bool { + guard k > 0 else { throw MillerRabinError.primeLowAccuracy } + guard n > 0 else { throw MillerRabinError.primeLowerBorder } + guard n > 3 else { return true } + + // return false for all even numbers bigger than 2 + if n % 2 == 0 { + return false + } + + let s: UInt = UInt((n - 1).trailingZeroBitCount) + let d: UInt = (n - 1) >> s + + guard UInt(pow(2.0, Double(s))) * d == n - 1 else { throw MillerRabinError.primeLowerBorder } + + /// Inspect whether a given witness will reveal the true identity of n. + func tryComposite(_ a: UInt, d: UInt, n: UInt) throws -> Bool? { + var x = try calculateModularExponentiation(base: a, exponent: d, modulus: n) + if x == 1 || x == (n - 1) { + return nil + } + for _ in 1.. UInt { + guard modulus > 1 else { return 0 } + guard !(modulus-1).multipliedReportingOverflow(by: (modulus-1)).overflow else { + throw MillerRabinError.uIntOverflow + } + + var result: UInt = 1 + var exponentCopy = exponent + var baseCopy = base % modulus + + while exponentCopy > 0 { + if exponentCopy % 2 == 1 { + result = (result * baseCopy) % modulus + } + exponentCopy = exponentCopy >> 1 + baseCopy = (baseCopy * baseCopy) % modulus + } + + return result +} diff --git a/Miller-Rabin Primality Test/MRPrimality.playground/playground.xcworkspace/contents.xcworkspacedata b/Miller-Rabin Primality Test/MRPrimality.playground/playground.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/Miller-Rabin Primality Test/MRPrimality.playground/playground.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Miller-Rabin Primality Test/MRPrimality.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Miller-Rabin Primality Test/MRPrimality.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Miller-Rabin Primality Test/MRPrimality.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Miller-Rabin Primality Test/MRPrimality.swift b/Miller-Rabin Primality Test/MRPrimality.swift index ee2ace8b9..24b9c674c 100644 --- a/Miller-Rabin Primality Test/MRPrimality.swift +++ b/Miller-Rabin Primality Test/MRPrimality.swift @@ -1,6 +1,6 @@ // // MRPrimality.swift -// +// // // Created by Sahn Cha on 2016. 10. 18.. // @@ -8,104 +8,93 @@ import Foundation -/* - Miller-Rabin Primality Test. - One of the most used algorithms for primality testing. - Since this is a probabilistic algorithm, it needs to be executed multiple times to increase accuracy. - - Inputs: - n: UInt64 { target integer to be tested for primality } - k: Int { optional. number of iterations } - - Outputs: - true { probably prime } - false { composite } - */ -public func mrPrimalityTest(_ n: UInt64, iteration k: Int = 1) -> Bool { - guard n > 2 && n % 2 == 1 else { return false } - - var d = n - 1 - var s = 0 - - while d % 2 == 0 { - d /= 2 - s += 1 - } - - let range = UInt64.max - UInt64.max % (n - 2) - var r: UInt64 = 0 - repeat { - arc4random_buf(&r, MemoryLayout.size(ofValue: r)) - } while r >= range - - r = r % (n - 2) + 2 - - for _ in 1 ... k { - var x = powmod64(r, d, n) - if x == 1 || x == n - 1 { continue } - - if s == 1 { s = 2 } - - for _ in 1 ... s - 1 { - x = powmod64(x, 2, n) - if x == 1 { return false } - if x == n - 1 { break } - } - - if x != n - 1 { return false } - } - - return true +enum MillerRabinError: Error { + case primeLowAccuracy + case primeLowerBorder + case uIntOverflow } /* - Calculates (base^exp) mod m. - - Inputs: - base: UInt64 { base } - exp: UInt64 { exponent } - m: UInt64 { modulus } - - Outputs: - the result - */ -private func powmod64(_ base: UInt64, _ exp: UInt64, _ m: UInt64) -> UInt64 { - if m == 1 { return 0 } + The Miller–Rabin test relies on an equality or set of equalities that + hold true for prime values, then checks whether or not they hold for + a number that we want to test for primality. + + - Parameter n: an odd integer to be tested for primality; + - Parameter k: a parameter that determines the accuracy of the test + - throws: Can throw an error of type `MillerRabinError`. + - Returns: composite if n is composite, otherwise probably prime +*/ +func checkWithMillerRabin(_ n: UInt, accuracy k: UInt = 1) throws -> Bool { + guard k > 0 else { throw MillerRabinError.primeLowAccuracy } + guard n > 0 else { throw MillerRabinError.primeLowerBorder } + guard n > 3 else { return true } + + // return false for all even numbers bigger than 2 + if n % 2 == 0 { + return false + } - var result: UInt64 = 1 - var b = base % m - var e = exp + let s: UInt = UInt((n - 1).trailingZeroBitCount) + let d: UInt = (n - 1) >> s + + guard UInt(pow(2.0, Double(s))) * d == n - 1 else { throw EncryptionError.primeLowerBorder } + + /// Inspect whether a given witness will reveal the true identity of n. + func tryComposite(_ a: UInt, d: UInt, n: UInt) throws -> Bool? { + var x = try calculateModularExponentiation(base: a, exponent: d, modulus: n) + if x == 1 || x == (n - 1) { + return nil + } + for _ in 1.. 0 { - if e % 2 == 1 { result = mulmod64(result, b, m) } - b = mulmod64(b, b, m) - e >>= 1 - } + for _ in 0.. UInt64 { - var result: UInt64 = 0 - var a = first - var b = second + Calculates the modular exponentiation based on `Applied Cryptography by Bruce Schneier.` + in `Schneier, Bruce (1996). Applied Cryptography: Protocols, Algorithms, + and Source Code in C, Second Edition (2nd ed.). Wiley. ISBN 978-0-471-11709-4.` + + - Parameter base: The natural base b. + - Parameter base: The natural exponent e. + - Parameter base: The natural modulus m. + - Throws: Can throw a `uIntOverflow` if the modulus' square exceeds the memory + limitations of UInt on the current system. + - Returns: The modular exponentiation c. +*/ +private func calculateModularExponentiation(base: UInt, exponent: UInt, modulus: UInt) throws -> UInt { + guard modulus > 1 else { return 0 } + guard !(modulus-1).multipliedReportingOverflow(by: (modulus-1)).overflow else { + throw MillerRabinError.uIntOverflow + } - while a != 0 { - if a % 2 == 1 { result = ((result % m) + (b % m)) % m } // This may overflow if 'm' is a 64bit number && both 'result' and 'b' are very close to but smaller than 'm'. - a >>= 1 - b = (b << 1) % m - } + var result: UInt = 1 + var exponentCopy = exponent + var baseCopy = base % modulus + + while exponentCopy > 0 { + if exponentCopy % 2 == 1 { + result = (result * baseCopy) % modulus + } + exponentCopy = exponentCopy >> 1 + baseCopy = (baseCopy * baseCopy) % modulus + } - return result + return result } diff --git a/Miller-Rabin Primality Test/README.markdown b/Miller-Rabin Primality Test/README.markdown index 07acb9e18..9cc677a2e 100644 --- a/Miller-Rabin Primality Test/README.markdown +++ b/Miller-Rabin Primality Test/README.markdown @@ -27,8 +27,8 @@ The following pseudo code is excerpted from Wikipedia[3]: ## Usage ```swift -mrPrimalityTest(7) // test if 7 is prime. (default iteration = 1) -mrPrimalityTest(7, iteration: 10) // test if 7 is prime && iterate 10 times. +checkWithMillerRabin(7) // test if 7 is prime. (default iteration = 1) +checkWithMillerRabin(7, accuracy: 10) // test if 7 is prime && iterate 10 times. ``` ## Reference @@ -37,6 +37,7 @@ mrPrimalityTest(7, iteration: 10) // test if 7 is prime && iterate 10 time 3. Miller–Rabin primality test - Wikipedia, the free encyclopedia _Written for Swift Algorithm Club by **Sahn Cha**, @scha00_ +_Code updated by **Simon C. Krüger**._ [1]: https://cs.uwaterloo.ca/research/tr/1975/CS-75-27.pdf [2]: http://www.sciencedirect.com/science/article/pii/0022314X80900840 diff --git a/Minimum Edit Distance/MinimumEditDistance.playground/Contents.swift b/Minimum Edit Distance/MinimumEditDistance.playground/Contents.swift index 967e73182..5db0aeb0d 100644 --- a/Minimum Edit Distance/MinimumEditDistance.playground/Contents.swift +++ b/Minimum Edit Distance/MinimumEditDistance.playground/Contents.swift @@ -1,15 +1,11 @@ // Minimum Edit Distance -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif extension String { public func minimumEditDistance(other: String) -> Int { - let m = self.characters.count - let n = other.characters.count + let m = self.count + let n = other.count var matrix = [[Int]](repeating: [Int](repeating: 0, count: n + 1), count: m + 1) // initialize matrix @@ -24,8 +20,8 @@ extension String { } // compute Levenshtein distance - for (i, selfChar) in self.characters.enumerated() { - for (j, otherChar) in other.characters.enumerated() { + for (i, selfChar) in self.enumerated() { + for (j, otherChar) in other.enumerated() { if otherChar == selfChar { // substitution of equal symbols with cost 0 matrix[i + 1][j + 1] = matrix[i][j] diff --git a/Minimum Edit Distance/README.markdown b/Minimum Edit Distance/README.markdown index b8880b2d4..c8e5e2f6d 100755 --- a/Minimum Edit Distance/README.markdown +++ b/Minimum Edit Distance/README.markdown @@ -37,8 +37,8 @@ Then in each cell the minimum of the cost of insertion, deletion, or substitutio ```swift // compute Levenshtein distance -for (i, selfChar) in self.characters.enumerated() { - for (j, otherChar) in other.characters.enumerated() { +for (i, selfChar) in self.enumerated() { + for (j, otherChar) in other.enumerated() { if otherChar == selfChar { // substitution of equal symbols with cost 0 matrix[i + 1][j + 1] = matrix[i][j] diff --git a/Minimum Spanning Tree (Unweighted)/MinimumSpanningTree.playground/Pages/Minimum spanning tree example.xcplaygroundpage/Contents.swift b/Minimum Spanning Tree (Unweighted)/MinimumSpanningTree.playground/Pages/Minimum spanning tree example.xcplaygroundpage/Contents.swift index 65aa9fbc4..8ad04379c 100644 --- a/Minimum Spanning Tree (Unweighted)/MinimumSpanningTree.playground/Pages/Minimum spanning tree example.xcplaygroundpage/Contents.swift +++ b/Minimum Spanning Tree (Unweighted)/MinimumSpanningTree.playground/Pages/Minimum spanning tree example.xcplaygroundpage/Contents.swift @@ -1,8 +1,3 @@ -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - func breadthFirstSearchMinimumSpanningTree(_ graph: Graph, source: Node) -> Graph { let minimumSpanningTree = graph.duplicate() diff --git a/Minimum Spanning Tree (Unweighted)/MinimumSpanningTree.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Minimum Spanning Tree (Unweighted)/MinimumSpanningTree.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Minimum Spanning Tree (Unweighted)/MinimumSpanningTree.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Minimum Spanning Tree (Unweighted)/Tests/Tests.xcodeproj/project.pbxproj b/Minimum Spanning Tree (Unweighted)/Tests/Tests.xcodeproj/project.pbxproj index a066b7a71..afb3b8acb 100644 --- a/Minimum Spanning Tree (Unweighted)/Tests/Tests.xcodeproj/project.pbxproj +++ b/Minimum Spanning Tree (Unweighted)/Tests/Tests.xcodeproj/project.pbxproj @@ -89,12 +89,12 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0720; - LastUpgradeCheck = 0720; + LastUpgradeCheck = 1000; ORGANIZATIONNAME = "Swift Algorithm Club"; TargetAttributes = { 7B2BBC7F1C779D720067B71D = { CreatedOnToolsVersion = 7.2; - LastSwiftMigration = 0820; + LastSwiftMigration = 1000; }; }; }; @@ -149,13 +149,23 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = 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_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_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "-"; @@ -193,13 +203,23 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = 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_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_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; CODE_SIGN_IDENTITY = "-"; @@ -218,6 +238,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.11; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; + SWIFT_COMPILATION_MODE = wholemodule; }; name = Release; }; @@ -231,7 +252,8 @@ PRODUCT_BUNDLE_IDENTIFIER = swift.algorithm.club.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.2; }; name = Debug; }; @@ -244,7 +266,8 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = swift.algorithm.club.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.2; }; name = Release; }; diff --git a/Minimum Spanning Tree (Unweighted)/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Minimum Spanning Tree (Unweighted)/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Minimum Spanning Tree (Unweighted)/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Minimum Spanning Tree (Unweighted)/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme b/Minimum Spanning Tree (Unweighted)/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme index 8ef8d8581..afd69e6a7 100644 --- a/Minimum Spanning Tree (Unweighted)/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme +++ b/Minimum Spanning Tree (Unweighted)/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme @@ -1,6 +1,6 @@ =4.0) -print("Hello, Swift 4!") -#endif - func minimumSpanningTreeKruskal(graph: Graph) -> (cost: Int, tree: Graph) { var cost: Int = 0 var tree = Graph() diff --git a/Minimum Spanning Tree/MinimumSpanningTree.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Minimum Spanning Tree/MinimumSpanningTree.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Minimum Spanning Tree/MinimumSpanningTree.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Monty Hall Problem/MontyHall.playground/Contents.swift b/Monty Hall Problem/MontyHall.playground/Contents.swift index 4bfff2340..a809410c7 100644 --- a/Monty Hall Problem/MontyHall.playground/Contents.swift +++ b/Monty Hall Problem/MontyHall.playground/Contents.swift @@ -1,10 +1,5 @@ //: Playground - noun: a place where people can play -// last checked with Xcode 9.0b4 -#if swift(>=4.0) - print("Hello, Swift 4!") -#endif - import Foundation func random(_ n: Int) -> Int { diff --git a/Multiset/Multiset.playground/Contents.swift b/Multiset/Multiset.playground/Contents.swift index b4f64ee1f..64b729df1 100644 --- a/Multiset/Multiset.playground/Contents.swift +++ b/Multiset/Multiset.playground/Contents.swift @@ -1,9 +1,5 @@ //: Playground - noun: a place where people can play -#if swift(>=4.0) - print("Hello, Swift 4!") -#endif - import Cocoa var set = Multiset() diff --git a/Myers Difference Algorithm/Images/EditGraph.png b/Myers Difference Algorithm/Images/EditGraph.png new file mode 100644 index 000000000..f31bb50a3 Binary files /dev/null and b/Myers Difference Algorithm/Images/EditGraph.png differ diff --git a/Myers Difference Algorithm/Images/EditGraph_k_move.png b/Myers Difference Algorithm/Images/EditGraph_k_move.png new file mode 100644 index 000000000..a8ea45c29 Binary files /dev/null and b/Myers Difference Algorithm/Images/EditGraph_k_move.png differ diff --git a/Myers Difference Algorithm/MyersDifferenceAlgorithm.playground/Contents.swift b/Myers Difference Algorithm/MyersDifferenceAlgorithm.playground/Contents.swift new file mode 100644 index 000000000..113487acc --- /dev/null +++ b/Myers Difference Algorithm/MyersDifferenceAlgorithm.playground/Contents.swift @@ -0,0 +1,22 @@ +//: Playground - noun: a place where people can play +import Foundation + +let shortestEditDistance: ([String], [String]) -> Int = MyersDifferenceAlgorithm.calculateShortestEditDistance(from:to:) + +/*** + All elements are same, so any scripts do not need. + So, the edit distance is 0 + ***/ +shortestEditDistance(["1", "2", "3"], ["1", "2", "3"]) + +/*** + Last element "3" should be inserted. + So, the edit distance is 1 + ***/ +shortestEditDistance(["1", "2"], ["1", "2", "3"]) + +/*** + First, remove "1", then insert "1" after "2". + So, the edit distance is 2 +***/ +shortestEditDistance(["1", "2", "3"], ["2", "1", "3"]) diff --git a/Myers Difference Algorithm/MyersDifferenceAlgorithm.playground/Sources/MyersDifferenceAlgorithm.swift b/Myers Difference Algorithm/MyersDifferenceAlgorithm.playground/Sources/MyersDifferenceAlgorithm.swift new file mode 100644 index 000000000..aedbf3e71 --- /dev/null +++ b/Myers Difference Algorithm/MyersDifferenceAlgorithm.playground/Sources/MyersDifferenceAlgorithm.swift @@ -0,0 +1,67 @@ +// +// MyersDifferenceAlgorithm.swift +// +// Created by Yuya Horita on 2018/02/27. +// Copyright © 2018年 hy. All rights reserved. +// + +import Foundation + +public struct MyersDifferenceAlgorithm { + public static func calculateShortestEditDistance(from fromArray: Array, to toArray: Array) -> Int { + let fromCount = fromArray.count + let toCount = toArray.count + let totalCount = toCount + fromCount + var furthestReaching = Array(repeating: 0, count: 2 * totalCount + 1) + + let isReachedAtSink: (Int, Int) -> Bool = { x, y in + return x == fromCount && y == toCount + } + + let snake: (Int, Int, Int) -> Int = { x, D, k in + var _x = x + while _x < fromCount && _x - k < toCount && fromArray[_x] == toArray[_x - k] { + _x += 1 + } + return _x + } + + for D in 0...totalCount { + for k in stride(from: -D, through: D, by: 2) { + let index = k + totalCount + + // (x, D, k) => the x position on the k_line where the number of scripts is D + // scripts means insertion or deletion + var x = 0 + if D == 0 { } + // k == -D, D will be the boundary k_line + // when k == -D, moving right on the Edit Graph(is delete script) from k - 1_line where D - 1 is unavailable. + // when k == D, moving bottom on the Edit Graph(is insert script) from k + 1_line where D - 1 is unavailable. + // furthestReaching x position has higher calculating priority. (x, D - 1, k - 1), (x, D - 1, k + 1) + else if k == -D || k != D && furthestReaching[index - 1] < furthestReaching[index + 1] { + // Getting initial x position + // ,using the furthestReaching X position on the k + 1_line where D - 1 + // ,meaning get (x, D, k) by (x, D - 1, k + 1) + moving bottom + snake + // this moving bottom on the edit graph is compatible with insert script + x = furthestReaching[index + 1] + } else { + // Getting initial x position + // ,using the futrhest X position on the k - 1_line where D - 1 + // ,meaning get (x, D, k) by (x, D - 1, k - 1) + moving right + snake + // this moving right on the edit graph is compatible with delete script + x = furthestReaching[index - 1] + 1 + } + + // snake + // diagonal moving can be performed with 0 cost. + // `same` script is needed ? + let _x = snake(x, D, k) + + if isReachedAtSink(_x, _x - k) { return D } + furthestReaching[index] = _x + } + } + + fatalError("Never comes here") + } +} diff --git a/Myers Difference Algorithm/MyersDifferenceAlgorithm.playground/contents.xcplayground b/Myers Difference Algorithm/MyersDifferenceAlgorithm.playground/contents.xcplayground new file mode 100644 index 000000000..5da2641c9 --- /dev/null +++ b/Myers Difference Algorithm/MyersDifferenceAlgorithm.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Myers Difference Algorithm/MyersDifferenceAlgorithm.swift b/Myers Difference Algorithm/MyersDifferenceAlgorithm.swift new file mode 100644 index 000000000..aedbf3e71 --- /dev/null +++ b/Myers Difference Algorithm/MyersDifferenceAlgorithm.swift @@ -0,0 +1,67 @@ +// +// MyersDifferenceAlgorithm.swift +// +// Created by Yuya Horita on 2018/02/27. +// Copyright © 2018年 hy. All rights reserved. +// + +import Foundation + +public struct MyersDifferenceAlgorithm { + public static func calculateShortestEditDistance(from fromArray: Array, to toArray: Array) -> Int { + let fromCount = fromArray.count + let toCount = toArray.count + let totalCount = toCount + fromCount + var furthestReaching = Array(repeating: 0, count: 2 * totalCount + 1) + + let isReachedAtSink: (Int, Int) -> Bool = { x, y in + return x == fromCount && y == toCount + } + + let snake: (Int, Int, Int) -> Int = { x, D, k in + var _x = x + while _x < fromCount && _x - k < toCount && fromArray[_x] == toArray[_x - k] { + _x += 1 + } + return _x + } + + for D in 0...totalCount { + for k in stride(from: -D, through: D, by: 2) { + let index = k + totalCount + + // (x, D, k) => the x position on the k_line where the number of scripts is D + // scripts means insertion or deletion + var x = 0 + if D == 0 { } + // k == -D, D will be the boundary k_line + // when k == -D, moving right on the Edit Graph(is delete script) from k - 1_line where D - 1 is unavailable. + // when k == D, moving bottom on the Edit Graph(is insert script) from k + 1_line where D - 1 is unavailable. + // furthestReaching x position has higher calculating priority. (x, D - 1, k - 1), (x, D - 1, k + 1) + else if k == -D || k != D && furthestReaching[index - 1] < furthestReaching[index + 1] { + // Getting initial x position + // ,using the furthestReaching X position on the k + 1_line where D - 1 + // ,meaning get (x, D, k) by (x, D - 1, k + 1) + moving bottom + snake + // this moving bottom on the edit graph is compatible with insert script + x = furthestReaching[index + 1] + } else { + // Getting initial x position + // ,using the futrhest X position on the k - 1_line where D - 1 + // ,meaning get (x, D, k) by (x, D - 1, k - 1) + moving right + snake + // this moving right on the edit graph is compatible with delete script + x = furthestReaching[index - 1] + 1 + } + + // snake + // diagonal moving can be performed with 0 cost. + // `same` script is needed ? + let _x = snake(x, D, k) + + if isReachedAtSink(_x, _x - k) { return D } + furthestReaching[index] = _x + } + } + + fatalError("Never comes here") + } +} diff --git a/Myers Difference Algorithm/README.md b/Myers Difference Algorithm/README.md new file mode 100644 index 000000000..6be30ecda --- /dev/null +++ b/Myers Difference Algorithm/README.md @@ -0,0 +1,162 @@ +# Myers Difference Algorithm + +Myers Difference Algorithm(MDA) is an algorithm that finds a longest common subsequence(LCS) or shortest edit scripts(SES) of two sequences. The common subsequence of two sequences is the sequence of elements that appear in the same order in both sequences. For example, let's assume you have two arrays: + +``` +let firstArray = [1, 2, 3] +let secondArray = [2, 3, 4] +``` + +The common subsequences of these two arrays are `[2]`, and `[2, 3]`. The longest common sequence in this case is `[2, 3]`. MDA can accomplish this in O(ND) time, where N is the sum of the lengths of the two sequences. + +## Finding the length of the Longest Common Subsequence with Myers Algorithm on Edit Graph + +### Edit Graph + +MDA uses an **Edit Graph** to solve the LCS/SES problem. Below is a illustration depicting an edit graph: + + + +The x-axis at the top of the graph represents one of the sequences, `X`. The y-axis at the left side of the graph represents the other sequence, `Y`. Hence, the two sequences in question is the following: + +``` +X = [A, B, C, A, B, B, A] +Y = [C, B, A, B, A, C] +``` + +MDA generates the edit graph through the following steps: + +1. Line the element of sequence `X` on the x axis. And do for `Y` on the y axis. +2. Make grid and vertex at each point in the grid (x, y), `x in [0, N] and y in [0, M]`. `N` is the length of sequence `X`, `M` is of `Y` +3. Line for `x - y = k`, this line called k-line. Black dot line is this and pink number is the value of k. +3. Check the points `(i, j)`, where `X[i] = Y[j]`, called match point, light green one. +4. Connect vertex `(i - 1, j - 1)` and vertex `(i, j)`, where `(i, j)` is match point, then diagonal edge appears. + +Each elements on the figure shows that, +- `Red number and dotted lines`: The red number is the value of k and dotted lines are k-line. +- `Green dots: The match points`, which is the point `(i, j)` where `X[i] == Y[j]` +- `Blue line`: The shortest path from source to sink, which is the path we are going to find finally. + +> **Note:** Here, the sequences' start index is 1 not 0, so `X[1] = A`, `Y[1] = C` + +We discuss about which path is the shortest from `source` to `sink`. Can move on the edges on the graph. I mean we can move on the grid, horizontal and vertical edges, and the diagonal edges. + +The movements are compatible with the `Edit Scripts`, insert or delete. The word `Edit Scripts` appeared here, as referred at Introduction, SES is Shortest Edit Scripts. + +Let's get back on track. On this edit graph, the horizontal movement to vertex `(i, j)` is compatible with the script `delete at index i from X`, the vertical movement to vertex `(i, j)` is compatible with the script `insert the element of Y at index j to immediately after the element of X at index i`. How about for the diagonal movement?. This movement to vertex `(i, j)` means `X[i] = Y[j]`, so no script needs. + +- horizontal movement -> delete +- vertical movement -> insert +- diagonal movement -> no script because both are same. + +Next, add cost 1 for non-diagonal movement, because they can be compatible with script. And 0 for diagonal movement, same means no script. + +The total cost for the minimum path, exploring from `source` to `sink`, is the same as the length of the Longest Common Subsequence or Shortest Edit Script. + +So, LCS/SES problem can be solved by finding the shortest path from `source` to `sink`. + +### Myers Algorithm + +As mentioned above, the problem of finding a shortest edit script can be reduced to finding a path from `source (0, 0)` to `sink (N, M)` with the fewest number of horizontal and vertical edges. Let `D-path` be a path starting at `source` that has exactly `D` non-diagonal edges, or must move non-diagonally D-times. + +For example, A 0-path consists solely of diagonal edges. This means both sequences are completely same. + +By a simple induction, D-path must consist of a (D-1)-path followed by a non-diagonal edge and then diagonal edges, which called `snake`. The minimum value of D is 0, both sequences being same. To the contrary, the maximum value of D is N + M because delete all elements from X and insert all elements from Y to X is the worst case edit scripts. For getting D, or the length of SES, running loop from 0 to N + M is enough. + +```swift +for D in 0...N + M +``` + +Next, thinking about, where is the furthest reaching point for D-path on k-line. Like below, moving horizontally from k-line reaches (k+1)-line, moving vertically from k-line reaches (k-1)-line. Red chalky line shows that. + + + +So, threre are several end points of D-path, or D-path can end on several k-line. We need the information to get the next path ((D+1)-path) as mentioned above. In fact, D-path must end on +k-line, where k in { -D, -D + 2, ....., D - 2, D }. This is so simple, starting point, `source` is `(0, 0)` on (k=0)-line. D is the number of non-diagonal edges and non-diagonal movement changes current k-line to (kpm1)-line. Because 0 is even number, if D is even number D-path will end on (even_k)-line, if D is odd number D-path will end on (odd_k)-line. + +Searching loop outline will be below. + +```swift +for D in 0...N + M { + for k in stride(from: -D, through: D, by: 2) { + //Find the end point of the furthest reaching D-path in k-line. + if furthestReachingX == N && furthestReachingY == M { + // The D-path is the shortest path + // D is the length of Shortest Edit Script + return + } + } +} +``` + +The D-path on k-line can be decomposed into +- a furthest reaching (D-1)-path on (k-1)-line, followed by a horizontal edge, followed by `snake`. +- a furthest reaching (D-1)-path on (k+1)-line, followed by a vertical edge, followed by `snake`. +as discussed above. + +The Myers Algorithm key point are these. +- D-path must end on k-line, where k in { -D, -D + 2, ....., D - 2, D } +- The D-path on k-line can be decomposed into two patterns + +thanks for these, the number of calculation become less. + +```swift +public struct MyersDifferenceAlgorithm { + public static func calculateShortestEditDistance(from fromArray: Array, to toArray: Array) -> Int { + let fromCount = fromArray.count + let toCount = toArray.count + let totalCount = toCount + fromCount + var furthestReaching = Array(repeating: 0, count: 2 * totalCount + 1) + + let isReachedAtSink: (Int, Int) -> Bool = { x, y in + return x == fromCount && y == toCount + } + + let snake: (Int, Int, Int) -> Int = { x, D, k in + var _x = x + while _x < fromCount && _x - k < toCount && fromArray[_x] == toArray[_x - k] { + _x += 1 + } + return _x + } + + for D in 0...totalCount { + for k in stride(from: -D, through: D, by: 2) { + let index = k + totalCount + + // (x, D, k) => the x position on the k_line where the number of scripts is D + // scripts means insertion or deletion + var x = 0 + if D == 0 { } + // k == -D, D will be the boundary k_line + // when k == -D, moving right on the Edit Graph(is delete script) from k - 1_line where D - 1 is unavailable. + // when k == D, moving bottom on the Edit Graph(is insert script) from k + 1_line where D - 1 is unavailable. + // furthestReaching x position has higher calculating priority. (x, D - 1, k - 1), (x, D - 1, k + 1) + else if k == -D || k != D && furthestReaching[index - 1] < furthestReaching[index + 1] { + // Getting initial x position + // ,using the furthestReaching X position on the k + 1_line where D - 1 + // ,meaning get (x, D, k) by (x, D - 1, k + 1) + moving bottom + snake + // this moving bottom on the edit graph is compatible with insert script + x = furthestReaching[index + 1] + } else { + // Getting initial x position + // ,using the futrhest X position on the k - 1_line where D - 1 + // ,meaning get (x, D, k) by (x, D - 1, k - 1) + moving right + snake + // this moving right on the edit graph is compatible with delete script + x = furthestReaching[index - 1] + 1 + } + + // snake + // diagonal moving can be performed with 0 cost. + // `same` script is needed ? + let _x = snake(x, D, k) + + if isReachedAtSink(_x, _x - k) { return D } + furthestReaching[index] = _x + } + } + + fatalError("Never comes here") + } +} +``` diff --git a/Ordered Array/OrderedArray.playground/Contents.swift b/Ordered Array/OrderedArray.playground/Contents.swift index 5b6be6420..e48af22e2 100644 --- a/Ordered Array/OrderedArray.playground/Contents.swift +++ b/Ordered Array/OrderedArray.playground/Contents.swift @@ -1,9 +1,6 @@ //: Playground - noun: a place where people can play -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif + public struct OrderedArray { fileprivate var array = [T]() diff --git a/Ordered Set/AppleOrderedSet.playground/Contents.swift b/Ordered Set/AppleOrderedSet.playground/Contents.swift deleted file mode 100644 index 4388c664b..000000000 --- a/Ordered Set/AppleOrderedSet.playground/Contents.swift +++ /dev/null @@ -1,13 +0,0 @@ -let s = AppleOrderedSet() - -s.add(1) -s.add(2) -s.add(-1) -s.add(0) -s.insert(4, at: 3) - -s.set(-1, at: 0) -s.remove(-1) - -print(s.object(at: 1)) - diff --git a/Ordered Set/AppleOrderedSet.playground/Sources/AppleOrderedSet.swift b/Ordered Set/AppleOrderedSet.playground/Sources/AppleOrderedSet.swift deleted file mode 100644 index 3ff19cb8d..000000000 --- a/Ordered Set/AppleOrderedSet.playground/Sources/AppleOrderedSet.swift +++ /dev/null @@ -1,72 +0,0 @@ -public class AppleOrderedSet { - private var objects: [T] = [] - private var indexOfKey: [T: Int] = [:] - - public init() {} - - // O(1) - public func add(_ object: T) { - guard indexOfKey[object] == nil else { - return - } - - objects.append(object) - indexOfKey[object] = objects.count - 1 - } - - // O(n) - public func insert(_ object: T, at index: Int) { - assert(index < objects.count, "Index should be smaller than object count") - assert(index >= 0, "Index should be bigger than 0") - - guard indexOfKey[object] == nil else { - return - } - - objects.insert(object, at: index) - indexOfKey[object] = index - for i in index+1.. T { - assert(index < objects.count, "Index should be smaller than object count") - assert(index >= 0, "Index should be bigger than 0") - - return objects[index] - } - - // O(1) - public func set(_ object: T, at index: Int) { - assert(index < objects.count, "Index should be smaller than object count") - assert(index >= 0, "Index should be bigger than 0") - - guard indexOfKey[object] == nil else { - return - } - - indexOfKey.removeValue(forKey: objects[index]) - indexOfKey[object] = index - objects[index] = object - } - - // O(1) - public func indexOf(_ object: T) -> Int { - return indexOfKey[object] ?? -1 - } - - // O(n) - public func remove(_ object: T) { - guard let index = indexOfKey[object] else { - return - } - - indexOfKey.removeValue(forKey: object) - objects.remove(at: index) - for i in index.. { - private var objects: [T] = [] - private var indexOfKey: [T: Int] = [:] - - public init() {} - - // O(1) - public func add(_ object: T) { - guard indexOfKey[object] == nil else { - return - } - - objects.append(object) - indexOfKey[object] = objects.count - 1 - } - - // O(n) - public func insert(_ object: T, at index: Int) { - assert(index < objects.count, "Index should be smaller than object count") - assert(index >= 0, "Index should be bigger than 0") - - guard indexOfKey[object] == nil else { - return - } - - objects.insert(object, at: index) - indexOfKey[object] = index - for i in index+1.. T { - assert(index < objects.count, "Index should be smaller than object count") - assert(index >= 0, "Index should be bigger than 0") - - return objects[index] - } - - // O(1) - public func set(_ object: T, at index: Int) { - assert(index < objects.count, "Index should be smaller than object count") - assert(index >= 0, "Index should be bigger than 0") - - guard indexOfKey[object] == nil else { - return - } - - indexOfKey.removeValue(forKey: objects[index]) - indexOfKey[object] = index - objects[index] = object - } - - // O(1) - public func indexOf(_ object: T) -> Int { - return indexOfKey[object] ?? -1 - } - - // O(n) - public func remove(_ object: T) { - guard let index = indexOfKey[object] else { - return - } - - indexOfKey.removeValue(forKey: object) - objects.remove(at: index) - for i in index..() + +s.add(1) +s.add(2) +s.add(-1) +s.add(0) +s.insert(4, at: 3) + +print(s.all()) // [1, 2, -1, 4, 0] + +s.set(-1, at: 0) // We already have -1 in index: 2, so we will do nothing here + +print(s.all()) // [1, 2, -1, 4, 0] + +s.remove(-1) + +print(s.all()) // [1, 2, 4, 0] + +print(s.object(at: 1)) // 2 + +print(s.object(at: 2)) // 4 + diff --git a/Ordered Set/OrderedSet.playground/Pages/Example 3.xcplaygroundpage/timeline.xctimeline b/Ordered Set/OrderedSet.playground/Pages/Example 3.xcplaygroundpage/timeline.xctimeline deleted file mode 100644 index bf468afec..000000000 --- a/Ordered Set/OrderedSet.playground/Pages/Example 3.xcplaygroundpage/timeline.xctimeline +++ /dev/null @@ -1,6 +0,0 @@ - - - - - diff --git a/Ordered Set/OrderedSet.playground/Sources/OrderedSet.swift b/Ordered Set/OrderedSet.playground/Sources/OrderedSet.swift index 8490a3320..5508c3637 100644 --- a/Ordered Set/OrderedSet.playground/Sources/OrderedSet.swift +++ b/Ordered Set/OrderedSet.playground/Sources/OrderedSet.swift @@ -1,117 +1,76 @@ -/* - An Ordered Set is a collection where all items in the set follow an ordering, - usually ordered from 'least' to 'most'. The way you value and compare items - can be user-defined. -*/ -public struct OrderedSet { - private var internalSet = [T]() - - public init() { } - - // Returns the number of elements in the OrderedSet. - public var count: Int { - return internalSet.count - } - - // Inserts an item. Performance: O(n) - public mutating func insert(_ item: T) { - if exists(item) { - return // don't add an item if it already exists - } - - // Insert new the item just before the one that is larger. - for i in 0.. item { - internalSet.insert(item, at: i) - return - } +public class OrderedSet { + private var objects: [T] = [] + private var indexOfKey: [T: Int] = [:] + + public init() {} + + // O(1) + public func add(_ object: T) { + guard indexOfKey[object] == nil else { + return } - - // Append to the back if the new item is greater than any other in the set. - internalSet.append(item) + + objects.append(object) + indexOfKey[object] = objects.count - 1 } - - // Removes an item if it exists. Performance: O(n) - public mutating func remove(_ item: T) { - if let index = index(of: item) { - internalSet.remove(at: index) + + // O(n) + public func insert(_ object: T, at index: Int) { + assert(index < objects.count, "Index should be smaller than object count") + assert(index >= 0, "Index should be bigger than 0") + + guard indexOfKey[object] == nil else { + return } - } - - // Returns true if and only if the item exists somewhere in the set. - public func exists(_ item: T) -> Bool { - return index(of: item) != nil - } - - // Returns the index of an item if it exists, or -1 otherwise. - public func index(of item: T) -> Int? { - var leftBound = 0 - var rightBound = count - 1 - - while leftBound <= rightBound { - let mid = leftBound + ((rightBound - leftBound) / 2) - - if internalSet[mid] > item { - rightBound = mid - 1 - } else if internalSet[mid] < item { - leftBound = mid + 1 - } else if internalSet[mid] == item { - return mid - } else { - // When we get here, we've landed on an item whose value is equal to the - // value of the item we're looking for, but the items themselves are not - // equal. We need to check the items with the same value to the right - // and to the left in order to find an exact match. - - // Check to the right. - for j in stride(from: mid, to: count - 1, by: 1) { - if internalSet[j + 1] == item { - return j + 1 - } else if internalSet[j] < internalSet[j + 1] { - break - } - } - - // Check to the left. - for j in stride(from: mid, to: 0, by: -1) { - if internalSet[j - 1] == item { - return j - 1 - } else if internalSet[j] > internalSet[j - 1] { - break - } - } - return nil - } + + objects.insert(object, at: index) + indexOfKey[object] = index + for i in index+1.. T { - assert(index >= 0 && index < count) - return internalSet[index] + + // O(1) + public func object(at index: Int) -> T { + assert(index < objects.count, "Index should be smaller than object count") + assert(index >= 0, "Index should be bigger than 0") + + return objects[index] } - - // Returns the 'maximum' or 'largest' value in the set. - public func max() -> T? { - return count == 0 ? nil : internalSet[count - 1] + + // O(1) + public func set(_ object: T, at index: Int) { + assert(index < objects.count, "Index should be smaller than object count") + assert(index >= 0, "Index should be bigger than 0") + + guard indexOfKey[object] == nil else { + return + } + + indexOfKey.removeValue(forKey: objects[index]) + indexOfKey[object] = index + objects[index] = object } - - // Returns the 'minimum' or 'smallest' value in the set. - public func min() -> T? { - return count == 0 ? nil : internalSet[0] + + // O(1) + public func indexOf(_ object: T) -> Int { + return indexOfKey[object] ?? -1 } - - // Returns the k-th largest element in the set, if k is in the range - // [1, count]. Returns nil otherwise. - public func kLargest(_ k: Int) -> T? { - return k > count || k <= 0 ? nil : internalSet[count - k] + + // O(n) + public func remove(_ object: T) { + guard let index = indexOfKey[object] else { + return + } + + indexOfKey.removeValue(forKey: object) + objects.remove(at: index) + for i in index.. T? { - return k > count || k <= 0 ? nil : internalSet[k - 1] + + public func all() -> [T] { + return objects } } diff --git a/Ordered Set/OrderedSet.playground/contents.xcplayground b/Ordered Set/OrderedSet.playground/contents.xcplayground index 18c02c912..5da2641c9 100644 --- a/Ordered Set/OrderedSet.playground/contents.xcplayground +++ b/Ordered Set/OrderedSet.playground/contents.xcplayground @@ -1,8 +1,4 @@ - - - - - - + + \ No newline at end of file diff --git a/Ordered Set/OrderedSet.swift b/Ordered Set/OrderedSet.swift index a8b3b0966..8b6df7d3e 100644 --- a/Ordered Set/OrderedSet.swift +++ b/Ordered Set/OrderedSet.swift @@ -1,117 +1,77 @@ -/* - An Ordered Set is a collection where all items in the set follow an ordering, - usually ordered from 'least' to 'most'. The way you value and compare items - can be user-defined. -*/ -public struct OrderedSet { - private var internalSet = [T]() - - public init() { } - - // Returns the number of elements in the OrderedSet. - public var count: Int { - return internalSet.count - } - - // Inserts an item. Performance: O(n) - public mutating func insert(item: T) { - if exists(item) { - return // don't add an item if it already exists +public class OrderedSet { + private var objects: [T] = [] + private var indexOfKey: [T: Int] = [:] + + public init() {} + + // O(1) + public func add(_ object: T) { + guard indexOfKey[object] == nil else { + return } - - // Insert new the item just before the one that is larger. - for i in 0.. item { - internalSet.insert(item, atIndex: i) - return - } - } - - // Append to the back if the new item is greater than any other in the set. - internalSet.append(item) + + objects.append(object) + indexOfKey[object] = objects.count - 1 } - - // Removes an item if it exists. Performance: O(n) - public mutating func remove(item: T) { - if let index = indexOf(item) { - internalSet.removeAtIndex(index) + + // O(n) + public func insert(_ object: T, at index: Int) { + assert(index < objects.count, "Index should be smaller than object count") + assert(index >= 0, "Index should be bigger than 0") + + guard indexOfKey[object] == nil else { + return } - } - - // Returns true if and only if the item exists somewhere in the set. - public func exists(item: T) -> Bool { - return indexOf(item) != nil - } - - // Returns the index of an item if it exists, or -1 otherwise. - public func indexOf(item: T) -> Int? { - var leftBound = 0 - var rightBound = count - 1 - - while leftBound <= rightBound { - let mid = leftBound + ((rightBound - leftBound) / 2) - - if internalSet[mid] > item { - rightBound = mid - 1 - } else if internalSet[mid] < item { - leftBound = mid + 1 - } else if internalSet[mid] == item { - return mid - } else { - // When we get here, we've landed on an item whose value is equal to the - // value of the item we're looking for, but the items themselves are not - // equal. We need to check the items with the same value to the right - // and to the left in order to find an exact match. - - // Check to the right. - for j in mid.stride(to: count - 1, by: 1) { - if internalSet[j + 1] == item { - return j + 1 - } else if internalSet[j] < internalSet[j + 1] { - break - } - } - - // Check to the left. - for j in mid.stride(to: 0, by: -1) { - if internalSet[j - 1] == item { - return j - 1 - } else if internalSet[j] > internalSet[j - 1] { - break - } - } - return nil - } + + objects.insert(object, at: index) + indexOfKey[object] = index + for i in index+1.. T { - assert(index >= 0 && index < count) - return internalSet[index] + + // O(1) + public func object(at index: Int) -> T { + assert(index < objects.count, "Index should be smaller than object count") + assert(index >= 0, "Index should be bigger than 0") + + return objects[index] } - - // Returns the 'maximum' or 'largest' value in the set. - public func max() -> T? { - return count == 0 ? nil : internalSet[count - 1] + + // O(1) + public func set(_ object: T, at index: Int) { + assert(index < objects.count, "Index should be smaller than object count") + assert(index >= 0, "Index should be bigger than 0") + + guard indexOfKey[object] == nil else { + return + } + + indexOfKey.removeValue(forKey: objects[index]) + indexOfKey[object] = index + objects[index] = object } - - // Returns the 'minimum' or 'smallest' value in the set. - public func min() -> T? { - return count == 0 ? nil : internalSet[0] + + // O(1) + public func indexOf(_ object: T) -> Int { + return indexOfKey[object] ?? -1 } - - // Returns the k-th largest element in the set, if k is in the range - // [1, count]. Returns nil otherwise. - public func kLargest(k: Int) -> T? { - return k > count || k <= 0 ? nil : internalSet[count - k] + + // O(n) + public func remove(_ object: T) { + guard let index = indexOfKey[object] else { + return + } + + indexOfKey.removeValue(forKey: object) + objects.remove(at: index) + for i in index.. T? { - return k > count || k <= 0 ? nil : internalSet[k - 1] + + public func all() -> [T] { + return objects } } + diff --git a/Ordered Set/README.markdown b/Ordered Set/README.markdown index bc5003a61..82d736731 100644 --- a/Ordered Set/README.markdown +++ b/Ordered Set/README.markdown @@ -1,337 +1,121 @@ # Ordered Set -An Ordered Set is a collection of unique items in sorted order. Items are usually sorted from least to greatest. +Let's look into how to implement [Ordered Set](https://developer.apple.com/documentation/foundation/nsorderedset). -The Ordered Set data type is a hybrid of: - -- a [Set](https://en.wikipedia.org/wiki/Set_%28mathematics%29), a collection of unique items where the order does not matter, and -- a [Sequence](https://en.wikipedia.org/wiki/Sequence), an ordered list of items where each item may appear more than once. - -It's important to keep in mind that two items can have the same *value* but still may not be equal. For example, we could define "a" and "z" to have the same value (their lengths), but clearly "a" != "z". - -## Why use an ordered set? - -Ordered Sets should be considered when you need to keep your collection sorted at all times, and you do lookups on the collection much more frequently than inserting or deleting items. Many of the lookup operations for an Ordered Set are **O(1)**. - -A good example would be keeping track of the rankings of players in a scoreboard (see example 2 below). - -#### These are ordered sets - -A set of integers: - - [1, 2, 3, 6, 8, 10, 1000] - -A set of strings: - - ["a", "is", "set", "this"] - -The "value" of these strings could be their text content, but also for example their length. - -#### These are not ordered sets - -This set violates the property of uniqueness: - - [1, 1, 2, 3, 5, 8] - -This set violates the sorted property: - - [1, 11, 2, 3] - -## The code - -We'll start by creating our internal representation for the Ordered Set. Since the idea of a set is similar to that of an array, we will use an array to represent our set. Furthermore, since we'll need to keep the set sorted, we need to be able to compare the individual elements. Thus, any type must conform to the [Comparable Protocol](https://developer.apple.com/library/watchos/documentation/Swift/Reference/Swift_Comparable_Protocol/index.html). - -```swift -public struct OrderedSet { - private var internalSet = [T]() - - // Returns the number of elements in the OrderedSet. - public var count: Int { - return internalSet.count - } - ... -``` - -Lets take a look at the `insert()` function first. This first checks if the item already exists in the collection. If so, it returns and does not insert the item. Otherwise, it will insert the item through straightforward iteration. +Here is the example about how it works ```swift - public mutating func insert(_ item: T){ - if exists(item) { - return // don't add an item if it already exists - } - - // Insert new the item just before the one that is larger. - for i in 0.. item { - internalSet.insert(item, at: i) - return - } - } - - // Append to the back if the new item is greater than any other in the set. - internalSet.append(item) - } -``` +let s = AppleOrderedSet() -As we'll see later on, checking if the item is already in the set has an efficiency of **O(log(n) + k)** where **k** is the number of items with the same value as the item we are inserting. +s.add(1) +s.add(2) +s.add(-1) +s.add(0) +s.insert(4, at: 3) -To insert the new item, the `for` loop starts from the beginning of the array, and checks to see if each item is larger than the item we want to insert. Once we find such an item, we insert the new one into its place. This shifts the rest of the array over to the right by 1 position. This loop is at worst **O(n)**. +print(s.all()) // [1, 2, -1, 4, 0] -The total performance of the `insert()` function is therefore **O(n)**. +s.set(-1, at: 0) // We already have -1 in index: 2, so we will do nothing here -Next up is the `remove()` function: +print(s.all()) // [1, 2, -1, 4, 0] -```swift - public mutating func remove(_ item: T) { - if let index = index(of: item) { - internalSet.remove(at: index) - } - } -``` +s.remove(-1) -First this checks if the item exists and then removes it from the array. Because of the `removeAtIndex()` function, the efficiency for remove is **O(n)**. +print(s.all()) // [1, 2, 4, 0] -The next function is `indexOf()`, which takes in an object of type `T` and returns the index of the corresponding item if it is in the set, or `nil` if it is not. Since our set is sorted, we can use a binary search to quickly search for the item. +print(s.object(at: 1)) // 2 -```swift - public func index(of item: T) -> Int? { - var leftBound = 0 - var rightBound = count - 1 - - while leftBound <= rightBound { - let mid = leftBound + ((rightBound - leftBound) / 2) - - if internalSet[mid] > item { - rightBound = mid - 1 - } else if internalSet[mid] < item { - leftBound = mid + 1 - } else if internalSet[mid] == item { - return mid - } else { - // see below - } - } - return nil - } +print(s.object(at: 2)) // 4 ``` -> **Note:** If you are not familiar with the concept of binary search, we have an [article that explains all about it](../Binary%20Search). - -However, there is an important issue to deal with here. Recall that two objects can be unequal yet still have the same "value" for the purposes of comparing them. Since a set can contain multiple items with the same value, it is important to check that the binary search has landed on the correct item. +The significant difference is the the array is not sorted. The elements in the array are the same when insert them. Image the array without duplicates and with `O(logn)` or `O(1)` search time. -For example, consider this ordered set of `Player` objects. Each `Player` has a name and a number of points: - - [ ("Bill", 50), ("Ada", 50), ("Jony", 50), ("Steve", 200), ("Jean-Louis", 500), ("Woz", 1000) ] - -We want the set to be ordered by points, from low to high. Multiple players can have the same number of points. The name of the player is not important for this ordering. However, the name *is* important for retrieving the correct item. - -Let's say we do `indexOf(bill)` where `bill` is player object `("Bill", 50)`. If we did a traditional binary search we'd land on index 2, which is the object `("Jony", 50)`. The value 50 matches, but it's not the object we're looking for! - -Therefore, we also need to check the items with the same value to the right and left of the midpoint. The code to check the left and right side looks like this: +The idea here is using a data structure to provide `O(1)` or `O(logn)` time complexity, so it's easy to think about hash table. ```swift - // Check to the right. - for j in mid.stride(to: count - 1, by: 1) { - if internalSet[j + 1] == item { - return j + 1 - } else if internalSet[j] < internalSet[j + 1] { - break - } - } - - // Check to the left. - for j in mid.stride(to: 0, by: -1) { - if internalSet[j - 1] == item { - return j - 1 - } else if internalSet[j] > internalSet[j - 1] { - break - } - } - - return nil +var indexOfKey: [T: Int] +var objects: [T] ``` -These loops start at the current `mid` value and then look at the neighboring values until we've found the correct object. +`indexOfKey` is used to track the index of the element. `objects` is array holding elements. -The combined runtime for `indexOf()` is **O(log(n) + k)** where **n** is the length of the set, and **k** is the number of items with the same *value* as the one that is being searched for. +We will go through some key functions details here. -Since the set is sorted, the following operations are all **O(1)**: +### Add -```swift - // Returns the 'maximum' or 'largest' value in the set. - public func max() -> T? { - return count == 0 ? nil : internalSet[count - 1] - } - - // Returns the 'minimum' or 'smallest' value in the set. - public func min() -> T? { - return count == 0 ? nil : internalSet[0] - } - - // Returns the k-th largest element in the set, if k is in the range - // [1, count]. Returns nil otherwise. - public func kLargest(_ k: Int) -> T? { - return k > count || k <= 0 ? nil : internalSet[count - k] - } - - // Returns the k-th smallest element in the set, if k is in the range - // [1, count]. Returns nil otherwise. - public func kSmallest(_ k: Int) -> T? { - return k > count || k <= 0 ? nil : internalSet[k - 1] - } -``` - -## Examples - -Below are a few examples that can be found in the playground file. - -### Example 1 - -Here we create a set with random Integers. Printing the largest/smallest 5 numbers in the set is fairly easy. +Update `indexOfKey` and insert element in the end of `objects` ```swift -// Example 1 with type Int -var mySet = OrderedSet() - -// Insert random numbers into the set -for _ in 0..<50 { - mySet.insert(randomNum(50, max: 500)) -} - -print(mySet) - -print(mySet.max()) -print(mySet.min()) - -// Print the 5 largest values -for k in 1...5 { - print(mySet.kLargest(k)) -} - -// Print the 5 lowest values -for k in 1...5 { - print(mySet.kSmallest(k)) +// O(1) +public func add(_ object: T) { + guard indexOfKey[object] == nil else { + return + } + + objects.append(object) + indexOfKey[object] = objects.count - 1 } ``` -### Example 2 +### Insert -In this example we take a look at something a bit more interesting. We define a `Player` struct as follows: +Insert in a random place of the array will cost `O(n)` time. ```swift -public struct Player: Comparable { - public var name: String - public var points: Int +// O(n) +public func insert(_ object: T, at index: Int) { + assert(index < objects.count, "Index should be smaller than object count") + assert(index >= 0, "Index should be bigger than 0") + + guard indexOfKey[object] == nil else { + return + } + + objects.insert(object, at: index) + indexOfKey[object] = index + for i in index+1.. Bool { - return x.name == y.name && x.points == y.points -} -``` +### Set -But `<` only compares the points: +If the `object` already existed in the `OrderedSet`, do nothing. Otherwise, we need to update the `indexOfkey` and `objects`. ```swift -func <(x: Player, y: Player) -> Bool { - return x.points < y.points +// O(1) +public func set(_ object: T, at index: Int) { + assert(index < objects.count, "Index should be smaller than object count") + assert(index >= 0, "Index should be bigger than 0") + + guard indexOfKey[object] == nil else { + return + } + + indexOfKey.removeValue(forKey: objects[index]) + indexOfKey[object] = index + objects[index] = object } ``` -Therefore, two `Player`s can each have the same value (the number of points), but are not guaranteed to be equal (they can have different names). +### Remove -We create a new set and insert 20 random players. The `Player()` constructor gives each player a random name and score: +Remove element in the array will cost `O(n)`. At the same time, we need to update all elements's index after the removed element. ```swift -var playerSet = OrderedSet() - -// Populate the set with random players. -for _ in 0..<20 { - playerSet.insert(Player()) +// O(n) +public func remove(_ object: T) { + guard let index = indexOfKey[object] else { + return + } + + indexOfKey.removeValue(forKey: object) + objects.remove(at: index) + for i in index..() - -repeatedSet.insert(Player(name: "Player 1", points: 100)) -repeatedSet.insert(Player(name: "Player 2", points: 100)) -repeatedSet.insert(Player(name: "Player 3", points: 100)) -repeatedSet.insert(Player(name: "Player 4", points: 100)) -repeatedSet.insert(Player(name: "Player 5", points: 100)) -repeatedSet.insert(Player(name: "Player 6", points: 50)) -repeatedSet.insert(Player(name: "Player 7", points: 200)) -repeatedSet.insert(Player(name: "Player 8", points: 250)) -repeatedSet.insert(Player(name: "Player 9", points: 25)) -``` - -Notice how several of these players have the same value of 100 points. - -The set looks something like this: - - [Player 9, Player 6, Player 1, Player 2, Player 3, Player 4, Player 5, Player 7, Player 8] - -The next line looks for `Player 2`: - -```swift -print(repeatedSet.index(of: Player(name: "Player 2", points: 100))) -``` - -After the binary search finishes, the value of `mid` is at index 5: - - [Player 9, Player 6, Player 1, Player 2, Player 3, Player 4, Player 5, Player 7, Player 8] - mid - -However, this is not `Player 2`. Both `Player 4` and `Player 2` have the same points, but a different name. The binary search only looked at the points, not the name. - -But we do know that `Player 2` must be either to the immediate left or the right of `Player 4`, so we check both sides of `mid`. We only need to look at the objects with the same value as `Player 4`. The others are replaced by `X`: - - [X, X, Player 1, Player 2, Player 3, Player 4, Player 5, X, X] - mid - -The code then first checks on the right of `mid` (where the `*` is): - - [X, X, Player 1, Player 2, Player 3, Player 4, Player 5, X, X] - mid * - -The right side did not contain the item, so we look at the left side: - - [X, X, Player 1, Player 2, Player 3, Player 4, Player 5, X, X] - * mid - - [X, X, Player 1, Player 2, Player 3, Player 4, Player 5, X, X] - * mid - -Finally, we've found `Player 2`, and return index 3. - -## See Also - -[Apple Ordered Set](AppleOrderedSet.playground) -@remlostime added an Apple version of `OrderedSet` - -*Written By Zain Humayun* +*Written By Kai Chen* diff --git a/Palindromes/Palindromes.playground/Contents.swift b/Palindromes/Palindromes.playground/Contents.swift index 44662745a..49f767982 100644 --- a/Palindromes/Palindromes.playground/Contents.swift +++ b/Palindromes/Palindromes.playground/Contents.swift @@ -1,50 +1,8 @@ //: Playground - noun: a place where people can play -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - import Foundation -/** - Validate that a string is a plaindrome - - parameter str: The string to validate - - returns: `true` if string is plaindrome, `false` if string is not - */ -func isPalindrome(_ str: String) -> Bool { - let strippedString = str.replacingOccurrences(of: "\\W", with: "", options: .regularExpression, range: nil) - let length = strippedString.characters.count - - if length > 1 { - return palindrome(strippedString.lowercased(), left: 0, right: length - 1) - } - return false -} - -/** - Compares a strings left side character against right side character following - - parameter str: The string to compare characters of - - parameter left: Index of left side to compare, must be less than or equal to right - - parameter right: Index of right side to compare, must be greater than or equal to left - - returns: `true` if left side and right side have all been compared and they all match, `false` if a left and right aren't equal - */ -private func palindrome(_ str: String, left: Int, right: Int) -> Bool { - if left >= right { - return true - } - - let lhs = str[str.index(str.startIndex, offsetBy: left)] - let rhs = str[str.index(str.startIndex, offsetBy: right)] - - if lhs != rhs { - return false - } - - return palindrome(str, left: left + 1, right: right - 1) -} - -//true +// true isPalindrome("A man, a plan, a canal, Panama!") isPalindrome("abbcbba") isPalindrome("racecar") diff --git a/Palindromes/Palindromes.playground/Sources/Palindrome.swift b/Palindromes/Palindromes.playground/Sources/Palindrome.swift new file mode 100644 index 000000000..3652dbb1f --- /dev/null +++ b/Palindromes/Palindromes.playground/Sources/Palindrome.swift @@ -0,0 +1,38 @@ +import Foundation + +/** + Validate that a string is a plaindrome + - parameter str: The string to validate + - returns: `true` if string is plaindrome, `false` if string is not + */ +public func isPalindrome(_ str: String) -> Bool { + let strippedString = str.replacingOccurrences(of: "\\W", with: "", options: .regularExpression, range: nil) + let length = strippedString.count + + if length > 1 { + return palindrome(strippedString.lowercased(), left: 0, right: length - 1) + } + return false +} + +/** + Compares a strings left side character against right side character following + - parameter str: The string to compare characters of + - parameter left: Index of left side to compare, must be less than or equal to right + - parameter right: Index of right side to compare, must be greater than or equal to left + - returns: `true` if left side and right side have all been compared and they all match, `false` if a left and right aren't equal + */ +private func palindrome(_ str: String, left: Int, right: Int) -> Bool { + if left >= right { + return true + } + + let lhs = str[str.index(str.startIndex, offsetBy: left)] + let rhs = str[str.index(str.startIndex, offsetBy: right)] + + if lhs != rhs { + return false + } + + return palindrome(str, left: left + 1, right: right - 1) +} diff --git a/Palindromes/Palindromes.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Palindromes/Palindromes.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Palindromes/Palindromes.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Palindromes/Palindromes.swift b/Palindromes/Palindromes.swift deleted file mode 100644 index 9ddd04c80..000000000 --- a/Palindromes/Palindromes.swift +++ /dev/null @@ -1,26 +0,0 @@ -import Foundation - -func isPalindrome(_ str: String) -> Bool { - let strippedString = str.replacingOccurrences(of: "\\W", with: "", options: .regularExpression, range: nil) - let length = strippedString.characters.count - - if length > 1 { - return palindrome(strippedString.lowercased(), left: 0, right: length - 1) - } - return false -} - -private func palindrome(_ str: String, left: Int, right: Int) -> Bool { - if left >= right { - return true - } - - let lhs = str[str.index(str.startIndex, offsetBy: left)] - let rhs = str[str.index(str.startIndex, offsetBy: right)] - - if lhs != rhs { - return false - } - - return palindrome(str, left: left + 1, right: right - 1) -} diff --git a/Palindromes/README.markdown b/Palindromes/README.markdown index 18518adbe..e4012d8da 100644 --- a/Palindromes/README.markdown +++ b/Palindromes/README.markdown @@ -26,7 +26,7 @@ Here is a recursive implementation of this in Swift: ```swift func isPalindrome(_ str: String) -> Bool { let strippedString = str.replacingOccurrences(of: "\\W", with: "", options: .regularExpression, range: nil) - let length = strippedString.characters.count + let length = strippedString.count if length > 1 { return palindrome(strippedString.lowercased(), left: 0, right: length - 1) @@ -104,4 +104,4 @@ racecar -> left index == right index -> return true [Palindrome Wikipedia](https://en.wikipedia.org/wiki/Palindrome) -*Written by [Joshua Alvarado](https://github.com/https://github.com/lostatseajoshua)* +*Written by [Joshua Alvarado](https://github.com/lostatseajoshua)* diff --git a/Palindromes/Test/Palindromes.swift b/Palindromes/Test/Palindrome.swift similarity index 93% rename from Palindromes/Test/Palindromes.swift rename to Palindromes/Test/Palindrome.swift index 9ddd04c80..16af14fdb 100644 --- a/Palindromes/Test/Palindromes.swift +++ b/Palindromes/Test/Palindrome.swift @@ -2,7 +2,7 @@ import Foundation func isPalindrome(_ str: String) -> Bool { let strippedString = str.replacingOccurrences(of: "\\W", with: "", options: .regularExpression, range: nil) - let length = strippedString.characters.count + let length = strippedString.count if length > 1 { return palindrome(strippedString.lowercased(), left: 0, right: length - 1) diff --git a/Palindromes/Test/Test.xcodeproj/project.pbxproj b/Palindromes/Test/Test.xcodeproj/project.pbxproj index 07aae3e63..f01f6f061 100644 --- a/Palindromes/Test/Test.xcodeproj/project.pbxproj +++ b/Palindromes/Test/Test.xcodeproj/project.pbxproj @@ -8,11 +8,11 @@ /* Begin PBXBuildFile section */ 9437D8841E0D960A00A38FB8 /* Test.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9437D8831E0D960A00A38FB8 /* Test.swift */; }; - 9437D88B1E0D969500A38FB8 /* Palindromes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9437D8791E0D948A00A38FB8 /* Palindromes.swift */; }; + 9437D88B1E0D969500A38FB8 /* Palindrome.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9437D8791E0D948A00A38FB8 /* Palindrome.swift */; }; /* End PBXBuildFile section */ /* Begin PBXFileReference section */ - 9437D8791E0D948A00A38FB8 /* Palindromes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Palindromes.swift; sourceTree = ""; }; + 9437D8791E0D948A00A38FB8 /* Palindrome.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Palindrome.swift; sourceTree = ""; }; 9437D8811E0D960A00A38FB8 /* Test.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = Test.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 9437D8831E0D960A00A38FB8 /* Test.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Test.swift; sourceTree = ""; }; 9437D8851E0D960A00A38FB8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; @@ -32,7 +32,7 @@ 9437D8651E0D945200A38FB8 = { isa = PBXGroup; children = ( - 9437D8791E0D948A00A38FB8 /* Palindromes.swift */, + 9437D8791E0D948A00A38FB8 /* Palindrome.swift */, 9437D8821E0D960A00A38FB8 /* Test */, 9437D86F1E0D945200A38FB8 /* Products */, ); @@ -82,11 +82,12 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0820; - LastUpgradeCheck = 0820; + LastUpgradeCheck = 1000; ORGANIZATIONNAME = "Joshua Alvarado"; TargetAttributes = { 9437D8801E0D960A00A38FB8 = { CreatedOnToolsVersion = 8.2; + LastSwiftMigration = 1000; ProvisioningStyle = Automatic; }; }; @@ -123,7 +124,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 9437D88B1E0D969500A38FB8 /* Palindromes.swift in Sources */, + 9437D88B1E0D969500A38FB8 /* Palindrome.swift in Sources */, 9437D8841E0D960A00A38FB8 /* Test.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -140,15 +141,23 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = 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_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -187,15 +196,23 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = 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_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -229,7 +246,8 @@ PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.2; }; name = Debug; }; @@ -243,7 +261,8 @@ PRODUCT_BUNDLE_IDENTIFIER = self.edu.Test; PRODUCT_NAME = "$(TARGET_NAME)"; SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.2; }; name = Release; }; @@ -266,6 +285,7 @@ 9437D8881E0D960A00A38FB8 /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; /* End XCConfigurationList section */ }; diff --git a/Palindromes/Test/Test.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Palindromes/Test/Test.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Palindromes/Test/Test.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Palindromes/Test/Test.xcodeproj/xcshareddata/xcschemes/Test.xcscheme b/Palindromes/Test/Test.xcodeproj/xcshareddata/xcschemes/Test.xcscheme index 9a1b39e69..72ae3d696 100644 --- a/Palindromes/Test/Test.xcodeproj/xcshareddata/xcschemes/Test.xcscheme +++ b/Palindromes/Test/Test.xcodeproj/xcshareddata/xcschemes/Test.xcscheme @@ -1,6 +1,6 @@ + + + + diff --git a/Points Lines Planes/Points Lines Planes/2D/Line2D.swift b/Points Lines Planes/Points Lines Planes/2D/Line2D.swift new file mode 100644 index 000000000..c23040b14 --- /dev/null +++ b/Points Lines Planes/Points Lines Planes/2D/Line2D.swift @@ -0,0 +1,141 @@ +// +// Line2D.swift +// Points Lines Planes +// +// Created by Jaap Wijnen on 24-10-17. +// + +struct Line2D: Equatable { + + var slope: Slope + var offset: Double + var direction: Direction + + enum Slope: Equatable { + case finite(slope: Double) + case infinite(offset: Double) + } + + enum Direction: Equatable { + case increasing + case decreasing + } + + init(from p1: Point2D, to p2: Point2D) { + if p1 == p2 { fatalError("Points can not be equal when creating a line between them") } + if p1.x == p2.x { + self.slope = .infinite(offset: p1.x) + self.offset = 0 + self.direction = p1.y < p2.y ? .increasing : .decreasing + + return + } + + let slope = (p1.y - p2.y)/(p1.x - p2.x) + self.slope = .finite(slope: slope) + offset = (p1.y + p2.y - slope * (p1.x + p2.x))/2 + if slope >= 0 { + // so a horizontal line going left to right is called increasing + self.direction = p1.x < p2.x ? .increasing : .decreasing + } else { + self.direction = p1.x < p2.x ? .decreasing : .increasing + } + } + + fileprivate init(slope: Slope, offset: Double, direction: Direction) { + self.slope = slope + self.offset = offset + self.direction = direction + } + + // returns y coordinate on line for given x + func y(at x: Double) -> Double { + switch self.slope { + case .finite(let slope): + return slope * x + self.offset + case .infinite: + fatalError("y can be anywhere on vertical line") + } + } + + // returns x coordinate on line for given y + func x(at y: Double) -> Double { + switch self.slope { + case .finite(let slope): + if slope == 0 { + fatalError("x can be anywhere on horizontal line") + } + return (y - self.offset)/slope + case .infinite(let offset): + return offset + } + } + + // finds intersection point between two lines. returns nil when lines don't intersect or lie on top of each other. + func intersect(with line: Line2D) -> Point2D? { + if self == line { return nil } + switch (self.slope, line.slope) { + case (.infinite, .infinite): + // lines are either parallel or on top of each other. + return nil + case (.finite(let slope1), .finite(let slope2)): + if slope1 == slope2 { return nil } // lines are parallel + // lines are not parallel calculate intersection point + let x = (line.offset - self.offset)/(slope1 - slope2) + let y = (slope1 + slope2) * x + self.offset + line.offset + return Point2D(x: x, y: y) + case (.infinite(let offset), .finite): + // one line is vertical so we only check what y value the other line has at that point + let x = offset + let y = line.y(at: x) + return Point2D(x: x, y: y) + case (.finite, .infinite(let offset)): + // one line is vertical so we only check what y value the other line has at that point + // lines are switched with respect to case above this one + let x = offset + let y = self.y(at: x) + return Point2D(x: x, y: y) + } + } + + // returns a line perpendicular to self at the given y coordinate + // direction of perpendicular lines always changes clockwise + func perpendicularLineAt(y: Double) -> Line2D { + return perpendicularLineAt(p: Point2D(x: self.x(at: y), y: y)) + } + + // returns a line perpendicular to self at the given x coordinate + // direction of perpendicular lines always changes clockwise + func perpendicularLineAt(x: Double) -> Line2D { + return perpendicularLineAt(p: Point2D(x: x, y: self.y(at: x))) + } + + private func perpendicularLineAt(p: Point2D) -> Line2D { + switch self.slope { + case .finite(let slope): + if slope == 0 { + // line is horizontal so new line will be vertical + let dir: Direction = self.direction == .increasing ? .decreasing : .increasing + return Line2D(slope: .infinite(offset: p.x), offset: 0, direction: dir) + } + + // line is neither horizontal nor vertical + // we make a new line through the point p with new slope -1/slope + let offset = (slope + 1/slope)*p.x + self.offset + + // determine direction of new line based on direction of the old line and its slope + let dir: Direction + switch self.direction { + case .increasing: + dir = slope > 0 ? .decreasing : .increasing + case .decreasing: + dir = slope > 0 ? .increasing : .decreasing + } + return Line2D(slope: .finite(slope: -1/slope), offset: offset, direction: dir) + case .infinite: + // line is vertical so new line will be horizontal + let dir: Direction = self.direction == .increasing ? .increasing : .decreasing + return Line2D(slope: .finite(slope: 0), offset: p.y, direction: dir) + } + } +} diff --git a/Points Lines Planes/Points Lines Planes/2D/Point2D.swift b/Points Lines Planes/Points Lines Planes/2D/Point2D.swift new file mode 100644 index 000000000..bbd8305f8 --- /dev/null +++ b/Points Lines Planes/Points Lines Planes/2D/Point2D.swift @@ -0,0 +1,36 @@ +// +// Point2D.swift +// Points Lines Planes +// +// Created by Jaap Wijnen on 24-10-17. +// + +struct Point2D: Equatable { + var x: Double + var y: Double + + // returns true if point is on or right of line + func isRight(of line: Line2D) -> Bool { + switch line.slope { + case .finite: + let y = line.y(at: self.x) + switch line.direction { + case .increasing: + return y >= self.y + case .decreasing: + return y <= self.y + } + case .infinite(let offset): + switch line.direction { + case .increasing: + return self.x >= offset + case .decreasing: + return self.x <= offset + } + } + } + + func isLeft(of line: Line2D) -> Bool { + return !self.isRight(of: line) + } +} diff --git a/Points Lines Planes/Points Lines Planes/main.swift b/Points Lines Planes/Points Lines Planes/main.swift new file mode 100644 index 000000000..02137fc9d --- /dev/null +++ b/Points Lines Planes/Points Lines Planes/main.swift @@ -0,0 +1,24 @@ +// +// main.swift +// Points Lines Planes +// +// Created by Jaap Wijnen on 23-10-17. +// + +let p0 = Point2D(x: 0, y: 2) +let p1 = Point2D(x: 1, y: 1) +let p2 = Point2D(x: 1, y: 3) +let p3 = Point2D(x: 3, y: 1) +let p4 = Point2D(x: 3, y: 3) + +let horLine = Line2D(from: p1, to: p3) +let verLine = Line2D(from: p1, to: p2) +let line45 = Line2D(from: p1, to: p4) + +print(horLine.intersect(with: verLine)) +print(p0.isRight(of: line45)) +print(p0.isRight(of: horLine)) +print(p0.isRight(of: verLine)) +let lineperp = horLine.perpendicularLineAt(x: 2) +print(lineperp) + diff --git a/Points Lines Planes/README.md b/Points Lines Planes/README.md new file mode 100644 index 000000000..2d36667ec --- /dev/null +++ b/Points Lines Planes/README.md @@ -0,0 +1,34 @@ +# Points Lines (Planes) +This implements data structures for points lines and planes(not yet) in (for now) 2D space and a few functions to play around with them. This was originally written to improve on the Convex Hull algorithm but I thought it might be a nice addition in itself. Im planning to add 3D implementations as well. + +# implementation +Two `struct`s are implemented the `Point2D` and `Line2D`. + +``` +struct Point2D: { + var x: Double + var y: double +} +``` + +``` +struct Line2D { + var slope: Slope + var offset: Double + var direction: Direction +} +``` +Here `Slope` is an enum to account for vertical lines. +slope is infinite for vertical lines, offset is the x coordinate where the line crosses the line `y=0`. +slope is finite for any other line and contains a double with the actual value of the slope. +``` +enum Slope { + case finite(slope: Double) + case infinite(offset: Double) +} +``` +`Line2D` also contains a `Direction` enum. This is introduced in order to make lines directional in order to be able to determine what is left and right of a certain line. It is `.increasing` if the line points in positive y direction and `.decreasing` if it points in the negative y direction. + +`Line2D`'s offset is the the y-coordinate where the line crosses the vertical `x=0` line. + +*Written for the Swift Algorithm Club by Jaap Wijnen.* diff --git a/QuadTree/QuadTree.playground/Contents.swift b/QuadTree/QuadTree.playground/Contents.swift index f0d8cb2ef..ad8ee31f8 100644 --- a/QuadTree/QuadTree.playground/Contents.swift +++ b/QuadTree/QuadTree.playground/Contents.swift @@ -1,8 +1,3 @@ -// last checked with Xcode 9.0b5 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - import Foundation let tree = QuadTree(rect: Rect(origin: Point(0, 0), size: Size(xLength: 10, yLength: 10))) diff --git a/QuadTree/README.md b/QuadTree/README.md index ff71a66f0..ff660f852 100644 --- a/QuadTree/README.md +++ b/QuadTree/README.md @@ -6,11 +6,11 @@ A quadtree is a [tree](https://github.com/raywenderlich/swift-algorithm-club/tre ### Problem -Consider the following problem: your need to store a number of points (each point is a pair of `X` and `Y` coordinates) and then you need to answer which points lie in a certain rectangular region. A naive solution would be to store the points inside an array and then iterate over the points and check each one individually. This solution runs in O(n) though. +Consider the following problem: your need to store a number of points (each point is a pair of `X` and `Y` coordinates) and then you need to answer which points lie in a certain rectangular region. A naive solution would be to store the points inside an array and then iterate over the points and check each one individually. This solution runs in O(n) though. ### A Better Approach -Quadrees are most commonly used to partition a two-dimensional space by recursively subdividing it into four regions(quadrants). Let's see how we can use a Quadtree to store the points. +Quadtrees are most commonly used to partition a two-dimensional space by recursively subdividing it into four regions(quadrants). Let's see how we can use a Quadtree to store the points. Each node in the tree represents a rectangular region and stores a limited number(`maxPointCapacity`) of points that all lie in its region. @@ -40,7 +40,7 @@ class QuadTreeNode { init(rect: Rect) { self.rect = rect } - + ... } @@ -52,11 +52,11 @@ extension QuadTreeNode { @discardableResult func add(point: Point) -> Bool { - - if !rect.containts(point: point) { + + if !rect.contains(point: point) { return false } - + switch type { case .internal(let children): // pass the point to one of the children @@ -75,7 +75,7 @@ extension QuadTreeNode { } return true } - + private func subdivide() { switch type { case .leaf: @@ -105,9 +105,9 @@ To find the points that lie in a given region we can now traverse the tree from class QuadTree { ... - + let root: QuadTreeNode - + public func points(inRect rect: Rect) -> [Point] { return root.points(inRect: rect) } @@ -115,23 +115,23 @@ class QuadTree { extension QuadTreeNode { func points(inRect rect: Rect) -> [Point] { - + // if the node's rect and the given rect don't intersect, return an empty array, // because there can't be any points that lie the node's (or its children's) rect and // in the given rect if !self.rect.intersects(rect: rect) { return [] } - + var result: [Point] = [] - + // collect the node's points that lie in the rect for point in points { - if rect.containts(point: point) { + if rect.contains(point: point) { result.append(point) } } - + switch type { case .leaf: break @@ -141,7 +141,7 @@ extension QuadTreeNode { result.append(contentsOf: childNode.points(inRect: rect)) } } - + return result } } @@ -154,7 +154,6 @@ Both adding a point and searching can still take up to O(n) in the worst case, s Displaying a large amount of objects in a MapView - a great use case for a Quadtree ([Thoughtbot Article](https://robots.thoughtbot.com/how-to-handle-large-amounts-of-data-on-maps)) -More info on [Wiki](https://en.wikipedia.org/wiki/Quadtree) +More info on [Wikipedia](https://en.wikipedia.org/wiki/Quadtree) *Written for Swift Algorithm Club by Timur Galimov* - diff --git a/Queue/Queue-Optimized.swift b/Queue/Queue-Optimized.swift index 7dff91698..3b17b98cb 100644 --- a/Queue/Queue-Optimized.swift +++ b/Queue/Queue-Optimized.swift @@ -23,7 +23,7 @@ public struct Queue { } public mutating func dequeue() -> T? { - guard head < array.count, let element = array[head] else { return nil } + guard let element = array[guarded: head] else { return nil } array[head] = nil head += 1 @@ -45,3 +45,12 @@ public struct Queue { } } } + +extension Array { + subscript(guarded idx: Int) -> Element? { + guard (startIndex..=4.0) -print("Hello, Swift 4!") -#endif - /* Queue diff --git a/Queue/Queue.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Queue/Queue.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Queue/Queue.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Queue/Tests/Tests.xcodeproj/project.pbxproj b/Queue/Tests/Tests.xcodeproj/project.pbxproj index a554fd16e..f927aba0d 100644 --- a/Queue/Tests/Tests.xcodeproj/project.pbxproj +++ b/Queue/Tests/Tests.xcodeproj/project.pbxproj @@ -83,12 +83,12 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0720; - LastUpgradeCheck = 0800; + LastUpgradeCheck = 1000; ORGANIZATIONNAME = "Swift Algorithm Club"; TargetAttributes = { 7B2BBC7F1C779D720067B71D = { CreatedOnToolsVersion = 7.2; - LastSwiftMigration = 0800; + LastSwiftMigration = 1000; }; }; }; @@ -141,14 +141,22 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = 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_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_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -187,14 +195,22 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = 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_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_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; @@ -226,7 +242,8 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = swift.algorithm.club.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.2; }; name = Debug; }; @@ -238,7 +255,8 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = swift.algorithm.club.Tests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_SWIFT3_OBJC_INFERENCE = Default; + SWIFT_VERSION = 4.2; }; name = Release; }; diff --git a/Queue/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Queue/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Queue/Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Queue/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme b/Queue/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme index 14f27f777..afd69e6a7 100644 --- a/Queue/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme +++ b/Queue/Tests/Tests.xcodeproj/xcshareddata/xcschemes/Tests.xcscheme @@ -1,6 +1,6 @@ (_ a: inout [T], low: Int, high: Int) -> Int var i = low for j in low..(_ a: inout [T], low: Int, high: Int) -> Int var i = low for j in low..(_ a: inout [T], low: Int, high: Int) -> Int { repeat { i += 1 } while a[i] < pivot if i < j { - swap(&a[i], &a[j]) + a.swapAt(i, j) } else { return j } diff --git a/README.markdown b/README.markdown index b15bd1527..3e0174576 100644 --- a/README.markdown +++ b/README.markdown @@ -1,4 +1,4 @@ -![Swift Algorithm Club](/Images/SwiftAlgorithm-410-transp.png) +![Swift Algorithm Club](Images/SwiftAlgorithm-410-transp.png) # Welcome to the Swift Algorithm Club! @@ -8,7 +8,7 @@ If you're a computer science student who needs to learn this stuff for exams -- The goal of this project is to **explain how algorithms work**. The focus is on clarity and readability of the code, not on making a reusable library that you can drop into your own projects. That said, most of the code should be ready for production use but you may need to tweak it to fit into your own codebase. -Code is compatible with **Xcode 9** and **Swift 4**. We'll keep this updated with the latest version of Swift. +Code is compatible with **Xcode 10** and **Swift 4.2**. We'll keep this updated with the latest version of Swift. If you're interested in a GitHub pages version of the repo, check out [this](https://aquarchitect.github.io/swift-algorithm-club/). :heart_eyes: **Suggestions and contributions are welcome!** :heart_eyes: @@ -73,6 +73,10 @@ Fast sorts: - [Merge Sort](Merge%20Sort/) - [Heap Sort](Heap%20Sort/) +Hybrid sorts: + +- [Introsort](Introsort/) + Special-purpose sorts: - [Counting Sort](Counting%20Sort/) @@ -96,7 +100,8 @@ Bad sorting algorithms (don't use these!): - [Convex Hull](Convex%20Hull/). - [Miller-Rabin Primality Test](Miller-Rabin%20Primality%20Test/). Is the number a prime number? - [MinimumCoinChange](MinimumCoinChange/). A showcase for dynamic programming. - +- [Genetic](Genetic/). A simple example on how to slowly mutate a value to its ideal form, in the context of biological evolution. +- [Myers Difference Algorithm](Myers%20Difference%20Algorithm/). Finding the longest common subsequence of two sequences. ### Mathematics - [Greatest Common Divisor (GCD)](GCD/). Special bonus: the least common multiple. @@ -115,6 +120,7 @@ Bad sorting algorithms (don't use these!): - Neural Networks - PageRank - [Naive Bayes Classifier](Naive%20Bayes%20Classifier/) +- [Simulated annealing](Simulated%20annealing/). Probabilistic technique for approximating the global maxima in a (often discrete) large search space. ## Data structures @@ -145,7 +151,7 @@ Most of the time using just the built-in `Array`, `Dictionary`, and `Set` types ### Lists - [Linked List](Linked%20List/). A sequence of data items connected through links. Covers both singly and doubly linked lists. -- [Skip-List](Skip-List/). Skip List is a probablistic data-structure with same logarithmic time bound and efficiency as AVL/ or Red-Black tree and provides a clever compromise to efficiently support search and update operations. +- [Skip-List](Skip-List/). Skip List is a probabilistic data-structure with same logarithmic time bound and efficiency as AVL/ or Red-Black tree and provides a clever compromise to efficiently support search and update operations. ### Trees @@ -156,14 +162,15 @@ Most of the time using just the built-in `Array`, `Dictionary`, and `Set` types - [Splay Tree](Splay%20Tree/). A self balancing binary search tree that enables fast retrieval of recently updated elements. - [Threaded Binary Tree](Threaded%20Binary%20Tree/). A binary tree that maintains a few extra variables for cheap and fast in-order traversals. - [Segment Tree](Segment%20Tree/). Can quickly compute a function over a portion of an array. - - [Lazy Propagation](https://github.com/raywenderlich/swift-algorithm-club/tree/master/Segment%20Tree/LazyPropagation) + - [Lazy Propagation](https://github.com/raywenderlich/swift-algorithm-club/tree/master/Segment%20Tree/LazyPropagation) - kd-Tree +- [Sparse Table](Sparse%20Table/). Another take on quickly computing a function over a portion of an array, but this time we'll make it even quicker!. - [Heap](Heap/). A binary tree stored in an array, so it doesn't use pointers. Makes a great priority queue. - Fibonacci Heap - [Trie](Trie/). A special type of tree used to store associative data structures. - [B-Tree](B-Tree/). A self-balancing search tree, in which nodes can have more than two children. - [QuadTree](QuadTree/). A tree with 4 children. -- [Octree](Octree/). A tree with 8 children. +- [Octree](Octree/). A tree with 8 children. ### Hashing @@ -200,40 +207,30 @@ A lot of software developer interview questions consist of algorithmic puzzles. - [Finding Palindromes](Palindromes/) - [Dining Philosophers](DiningPhilosophers/) - [Egg Drop Problem](Egg%20Drop%20Problem/) +- [Encoding and Decoding Binary Tree](Encode%20and%20Decode%20Tree/) +- [Closest Pair](Closest%20Pair/) + ## Learn more! -For more information, check out these great books: +Like what you see? Check out [Data Structures & Algorithms in Swift](https://store.raywenderlich.com/products/data-structures-and-algorithms-in-swift), the official book by the Swift Algorithm Club team! -- [Introduction to Algorithms](https://mitpress.mit.edu/books/introduction-algorithms) by Cormen, Leiserson, Rivest, Stein -- [The Algorithm Design Manual](http://www.algorist.com) by Skiena -- [Elements of Programming Interviews](http://elementsofprogramminginterviews.com) by Aziz, Lee, Prakash -- [Algorithms](http://www.cs.princeton.edu/~rs/) by Sedgewick -- [Grokking Algorithms](https://www.manning.com/books/grokking-algorithms) by Aditya Bhargava +![Data Structures & Algorithms in Swift Book](Images/DataStructuresAndAlgorithmsInSwiftBook.png) -The following books are available for free online: +You’ll start with the fundamental structures of linked lists, queues and stacks, and see how to implement them in a highly Swift-like way. Move on to working with various types of trees, including general purpose trees, binary trees, AVL trees, binary search trees, and tries. -- [Algorithms](http://www.beust.com/algorithms.pdf) by Dasgupta, Papadimitriou, Vazirani -- [Algorithms, Etc.](http://jeffe.cs.illinois.edu/teaching/algorithms/) by Erickson -- [Algorithms + Data Structures = Programs](http://www.ethoberon.ethz.ch/WirthPubl/AD.pdf) by Wirth -- Algorithms and Data Structures: The Basic Toolbox by Mehlhorn and Sanders -- [Open Data Structures](http://opendatastructures.org) by Pat Morin -- [Wikibooks: Algorithms and Implementations](https://en.wikibooks.org/wiki/Algorithm_Implementation) +Go beyond bubble and insertion sort with better-performing algorithms, including mergesort, radix sort, heap sort, and quicksort. Learn how to construct directed, non-directed and weighted graphs to represent many real-world models, and traverse graphs and trees efficiently with breadth-first, depth-first, Dijkstra’s and Prim’s algorithms to solve problems such as finding the shortest path or lowest cost in a network. -Other algorithm repositories: +By the end of this book, you’ll have hands-on experience solving common issues with data structures and algorithms — and you’ll be well on your way to developing your own efficient and useful implementations! -- [EKAlgorithms](https://github.com/EvgenyKarkan/EKAlgorithms). A great collection of algorithms in Objective-C. -- [@lorentey](https://github.com/lorentey/). Production-quality Swift implementations of common algorithms and data structures. -- [Rosetta Code](http://rosettacode.org). Implementations in pretty much any language you can think of. -- [AlgorithmVisualizer](http://jasonpark.me/AlgorithmVisualizer/). Visualize algorithms on your browser. -- [Swift Structures](https://github.com/waynewbishop/SwiftStructures) Data Structures with directions on how to use them [here](http://waynewbishop.com/swift) +You can find the book on the [raywenderlich.com store](https://store.raywenderlich.com/products/data-structures-and-algorithms-in-swift). ## Credits The Swift Algorithm Club was originally created by [Matthijs Hollemans](https://github.com/hollance). -It is now maintained by [Vincent Ngo](https://www.raywenderlich.com/u/jomoka), [Kelvin Lau](https://github.com/kelvinlauKL) and [Ross O'brien](https://www.raywenderlich.com/u/narrativium). +It is now maintained by [Vincent Ngo](https://www.raywenderlich.com/u/jomoka), [Kelvin Lau](https://github.com/kelvinlauKL), and [Richard Ash](https://github.com/richard-ash). -The Swift Algorithm Club is a collaborative effort from the [most algorithmic members](https://github.com/rwenderlich/swift-algorithm-club/graphs/contributors) of the [raywenderlich.com](https://www.raywenderlich.com) community. We're always looking for help - why not [join the club](.github/CONTRIBUTING.md)? :] +The Swift Algorithm Club is a collaborative effort from the [most algorithmic members](https://github.com/raywenderlich/swift-algorithm-club/graphs/contributors) of the [raywenderlich.com](https://www.raywenderlich.com) community. We're always looking for help - why not [join the club](.github/CONTRIBUTING.md)? :] ## License diff --git a/Rabin-Karp/README.markdown b/Rabin-Karp/README.markdown index 79699caa8..a618d3981 100644 --- a/Rabin-Karp/README.markdown +++ b/Rabin-Karp/README.markdown @@ -1,6 +1,6 @@ # Rabin-Karp string search algorithm -The Rabin-Karp string search alogrithm is used to search text for a pattern. +The Rabin-Karp string search algorithm is used to search text for a pattern. A practical application of the algorithm is detecting plagiarism. Given source material, the algorithm can rapidly search through a paper for instances of sentences from the source material, ignoring details such as case and punctuation. Because of the abundance of the sought strings, single-string searching algorithms are impractical. @@ -12,10 +12,10 @@ at a time (e.g. "he ") and subtracts out the previous hash from the "T". ## Algorithm -The Rabin-Karp alogrithm uses a sliding window the size of the search pattern. It starts by hashing the search pattern, then +The Rabin-Karp algorithm uses a sliding window the size of the search pattern. It starts by hashing the search pattern, then hashing the first x characters of the text string where x is the length of the search pattern. It then slides the window one character over and uses the previous hash value to calculate the new hash faster. Only when it finds a hash that matches the hash of the search pattern will it compare -the two strings it see if they are the same (prevent a hash collision from producing a false positive) +the two strings it see if they are the same (to prevent a hash collision from producing a false positive). ## The code @@ -24,8 +24,8 @@ The major search method is next. More implementation details are in rabin-karp. ```swift public func search(text: String , pattern: String) -> Int { // convert to array of ints - let patternArray = pattern.characters.flatMap { $0.asInt } - let textArray = text.characters.flatMap { $0.asInt } + let patternArray = pattern.flatMap { $0.asInt } + let textArray = text.flatMap { $0.asInt } if textArray.count < patternArray.count { return -1 @@ -37,7 +37,7 @@ public func search(text: String , pattern: String) -> Int { let firstHash = hash(array: firstChars) if (patternHash == firstHash) { - // Verify this was not a hash collison + // Verify this was not a hash collision if firstChars == patternArray { return 0 } @@ -76,4 +76,4 @@ This will return 13 since ump is in the 13 position of the zero based string. [Rabin-Karp Wikipedia](https://en.wikipedia.org/wiki/Rabin%E2%80%93Karp_algorithm) -*Written by [Bill Barbour](https://github.com/brbatwork)* \ No newline at end of file +*Written by [Bill Barbour](https://github.com/brbatwork)* diff --git a/Rabin-Karp/Rabin-Karp.playground/Contents.swift b/Rabin-Karp/Rabin-Karp.playground/Contents.swift index c0a99339a..e087caa49 100644 --- a/Rabin-Karp/Rabin-Karp.playground/Contents.swift +++ b/Rabin-Karp/Rabin-Karp.playground/Contents.swift @@ -1,6 +1,6 @@ //: Taking our rabin-karp algorithm for a walk -// last checked with Xcode 9.0b4 +// last checked with Xcode 9.4 #if swift(>=4.0) print("Hello, Swift 4!") #endif @@ -8,7 +8,7 @@ print("Hello, Swift 4!") import UIKit struct Constants { - static let hashMultiplier = 69069 + static let hashMultiplier = 69061 } precedencegroup PowerPrecedence { higherThan: MultiplicationPrecedence } @@ -30,8 +30,8 @@ extension Character { // Find first position of pattern in the text using Rabin Karp algorithm public func search(text: String, pattern: String) -> Int { // convert to array of ints - let patternArray = pattern.characters.flatMap { $0.asInt } - let textArray = text.characters.flatMap { $0.asInt } + let patternArray = pattern.compactMap { $0.asInt } + let textArray = text.compactMap { $0.asInt } if textArray.count < patternArray.count { return -1 diff --git a/Rabin-Karp/Rabin-Karp.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Rabin-Karp/Rabin-Karp.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Rabin-Karp/Rabin-Karp.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Radix Sort/RadixSort.playground/Contents.swift b/Radix Sort/RadixSort.playground/Contents.swift index 941723c37..0b549fc9e 100644 --- a/Radix Sort/RadixSort.playground/Contents.swift +++ b/Radix Sort/RadixSort.playground/Contents.swift @@ -1,21 +1,10 @@ //: Playground - noun: a place where people can play -import Cocoa - -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - // Test Radix Sort with small array of ten values var array: [Int] = [19, 4242, 2, 9, 912, 101, 55, 67, 89, 32] radixSort(&array) // Test Radix Sort with large array of 1000 random values -var bigArray = [Int](repeating: 0, count: 1000) -var i = 0 -while i < 1000 { - bigArray[i] = Int(arc4random_uniform(1000) + 1) - i += 1 -} +var bigArray = (0..<1000).map { _ in Int.random(in: 1...1000) } +bigArray radixSort(&bigArray) diff --git a/Radix Sort/RadixSort.playground/Sources/radixSort.swift b/Radix Sort/RadixSort.playground/Sources/radixSort.swift index 424d33fb9..af382f481 100644 --- a/Radix Sort/RadixSort.playground/Sources/radixSort.swift +++ b/Radix Sort/RadixSort.playground/Sources/radixSort.swift @@ -3,7 +3,6 @@ Sorting Algorithm that sorts an input array of integers digit by digit. */ -import Foundation // NOTE: This implementation does not handle negative numbers public func radixSort(_ array: inout [Int] ) { diff --git a/Radix Sort/Tests/RadixSortTests.swift b/Radix Sort/Tests/RadixSortTests.swift index 9b881318a..688bcdcb8 100644 --- a/Radix Sort/Tests/RadixSortTests.swift +++ b/Radix Sort/Tests/RadixSortTests.swift @@ -9,13 +9,6 @@ import XCTest class RadixSortTests: XCTestCase { - func testSwift4() { - // last checked with Xcode 9.0b4 - #if swift(>=4.0) - print("Hello, Swift 4!") - #endif - } - func testCombSort() { let expectedSequence: [Int] = [2, 9, 19, 32, 55, 67, 89, 101, 912, 4242] var sequence = [19, 4242, 2, 9, 912, 101, 55, 67, 89, 32] diff --git a/Radix Tree/RadixTree.playground/Sources/RadixTree.swift b/Radix Tree/RadixTree.playground/Sources/RadixTree.swift index 1914f57bc..77e289f7d 100644 --- a/Radix Tree/RadixTree.playground/Sources/RadixTree.swift +++ b/Radix Tree/RadixTree.playground/Sources/RadixTree.swift @@ -181,10 +181,10 @@ public class RadixTree { else if shared == e.label { currEdge = e var tempIndex = searchStr.startIndex - for _ in 1...shared.characters.count { - tempIndex = searchStr.characters.index(after: tempIndex) + for _ in 1...shared.count { + tempIndex = searchStr.index(after: tempIndex) } - searchStr = searchStr.substring(from: tempIndex) + searchStr = String(searchStr[tempIndex...]) found = true break } @@ -192,18 +192,18 @@ public class RadixTree { // If the child's label and the search string share a partial prefix, // then both the label and the search string need to be substringed // and a new branch needs to be created - else if shared.characters.count > 0 { - var labelIndex = e.label.characters.startIndex + else if shared.count > 0 { + var labelIndex = e.label.startIndex // Create index objects and move them to after the shared prefix - for _ in 1...shared.characters.count { - index = searchStr.characters.index(after: index) - labelIndex = e.label.characters.index(after: labelIndex) + for _ in 1...shared.count { + index = searchStr.index(after: index) + labelIndex = e.label.index(after: labelIndex) } // Substring both the search string and the label from the shared prefix - searchStr = searchStr.substring(from: index) - e.label = e.label.substring(from: labelIndex) + searchStr = String(searchStr[index...]) + e.label = String(e.label[labelIndex...]) // Create 2 new edges and update parent/children values let newEdge = Edge(e.label) @@ -266,16 +266,16 @@ public class RadixTree { if shared == c.label { currEdge = c var tempIndex = searchStr.startIndex - for _ in 1...shared.characters.count { - tempIndex = searchStr.characters.index(after: tempIndex) + for _ in 1...shared.count { + tempIndex = searchStr.index(after: tempIndex) } - searchStr = searchStr.substring(from: tempIndex) + searchStr = String(searchStr[tempIndex...]) found = true break } // If the shared string is empty, go to the next child - else if shared.characters.count == 0 { + else if shared.count == 0 { continue } @@ -287,7 +287,7 @@ public class RadixTree { // If the search string and the child's label only share some characters, // the string is not in the tree, return false else if shared[shared.startIndex] == c.label[c.label.startIndex] && - shared.characters.count < c.label.characters.count { + shared.count < c.label.count { return false } } @@ -340,10 +340,10 @@ public class RadixTree { if shared == currEdge.children[c].label { currEdge = currEdge.children[c] var tempIndex = searchStr.startIndex - for _ in 1...shared.characters.count { - tempIndex = searchStr.characters.index(after: tempIndex) + for _ in 1...shared.count { + tempIndex = searchStr.index(after: tempIndex) } - searchStr = searchStr.substring(from: tempIndex) + searchStr = String(searchStr[tempIndex...]) found = true break } @@ -366,13 +366,13 @@ public class RadixTree { // i.e. sharedPrefix("court", "coral") -> "co" public func sharedPrefix(_ str1: String, _ str2: String) -> String { var temp = "" - var c1 = str1.characters.startIndex - var c2 = str2.characters.startIndex - for _ in 0...min(str1.characters.count-1, str2.characters.count-1) { + var c1 = str1.startIndex + var c2 = str2.startIndex + for _ in 0...min(str1.count-1, str2.count-1) { if str1[c1] == str2[c2] { temp.append( str1[c1] ) - c1 = str1.characters.index(after:c1) - c2 = str2.characters.index(after:c2) + c1 = str1.index(after:c1) + c2 = str2.index(after:c2) } else { return temp } diff --git a/Radix Tree/RadixTree.swift b/Radix Tree/RadixTree.swift index 958ffb4e6..943000dd3 100644 --- a/Radix Tree/RadixTree.swift +++ b/Radix Tree/RadixTree.swift @@ -181,10 +181,10 @@ public class RadixTree { else if shared == e.label { currEdge = e var tempIndex = searchStr.startIndex - for _ in 1...shared.characters.count { - tempIndex = searchStr.characters.index(after: tempIndex) + for _ in 1...shared.count { + tempIndex = searchStr.index(after: tempIndex) } - searchStr = searchStr.substring(from: tempIndex) + searchStr = String(searchStr[tempIndex...]) found = true break } @@ -192,18 +192,18 @@ public class RadixTree { // If the child's label and the search string share a partial prefix, // then both the label and the search string need to be substringed // and a new branch needs to be created - else if shared.characters.count > 0 { - var labelIndex = e.label.characters.startIndex + else if shared.count > 0 { + var labelIndex = e.label.startIndex // Create index objects and move them to after the shared prefix - for _ in 1...shared.characters.count { - index = searchStr.characters.index(after: index) - labelIndex = e.label.characters.index(after: labelIndex) + for _ in 1...shared.count { + index = searchStr.index(after: index) + labelIndex = e.label.index(after: labelIndex) } // Substring both the search string and the label from the shared prefix - searchStr = searchStr.substring(from: index) - e.label = e.label.substring(from: labelIndex) + searchStr = String(searchStr[index...]) + e.label = String(e.label[labelIndex...]) // Create 2 new edges and update parent/children values let newEdge = Edge(e.label) @@ -266,16 +266,16 @@ public class RadixTree { if shared == c.label { currEdge = c var tempIndex = searchStr.startIndex - for _ in 1...shared.characters.count { - tempIndex = searchStr.characters.index(after: tempIndex) + for _ in 1...shared.count { + tempIndex = searchStr.index(after: tempIndex) } - searchStr = searchStr.substring(from: tempIndex) + searchStr = String(searchStr[tempIndex...]) found = true break } // If the shared string is empty, go to the next child - else if shared.characters.count == 0 { + else if shared.count == 0 { continue } @@ -287,7 +287,7 @@ public class RadixTree { // If the search string and the child's label only share some characters, // the string is not in the tree, return false else if shared[shared.startIndex] == c.label[c.label.startIndex] && - shared.characters.count < c.label.characters.count { + shared.count < c.label.count { return false } } @@ -340,10 +340,10 @@ public class RadixTree { if shared == currEdge.children[c].label { currEdge = currEdge.children[c] var tempIndex = searchStr.startIndex - for _ in 1...shared.characters.count { - tempIndex = searchStr.characters.index(after: tempIndex) + for _ in 1...shared.count { + tempIndex = searchStr.index(after: tempIndex) } - searchStr = searchStr.substring(from: tempIndex) + searchStr = String(searchStr[tempIndex...]) found = true break } @@ -366,13 +366,13 @@ public class RadixTree { // i.e. sharedPrefix("court", "coral") -> "co" public func sharedPrefix(_ str1: String, _ str2: String) -> String { var temp = "" - var c1 = str1.characters.startIndex - var c2 = str2.characters.startIndex - for _ in 0...min(str1.characters.count-1, str2.characters.count-1) { + var c1 = str1.startIndex + var c2 = str2.startIndex + for _ in 0...min(str1.count-1, str2.count-1) { if str1[c1] == str2[c2] { temp.append( str1[c1] ) - c1 = str1.characters.index(after:c1) - c2 = str2.characters.index(after:c2) + c1 = str1.index(after:c1) + c2 = str2.index(after:c2) } else { return temp } diff --git a/Red-Black Tree/README.markdown b/Red-Black Tree/README.markdown index 6db827ea6..2108b9055 100644 --- a/Red-Black Tree/README.markdown +++ b/Red-Black Tree/README.markdown @@ -29,6 +29,7 @@ From [CLRS] ## Methods Nodes: +* `nodeX.getPredecessor()` Returns the inorder predecessor of nodeX * `nodeX.getSuccessor()` Returns the inorder successor of nodeX * `nodeX.minimum()` Returns the node with the minimum key of the subtree of nodeX * `nodeX.maximum()` Returns the node with the maximum key of the subtree of nodeX @@ -189,4 +190,4 @@ The overall runtime of delete is O(log n). ## Resources: [CLRS] T. Cormen, C. Leiserson, R. Rivest, and C. Stein. "Introduction to Algorithms", Third Edition. 2009 -*Written for Swift Algorithm Club by Ute Schiehlen. Updated from Jaap Wijnen and Ashwin Raghuraman's contributions.* +*Written for Swift Algorithm Club by Ute Schiehlen. Updated from Jaap Wijnen and Ashwin Raghuraman's contributions. Swift 4.2 check by Bruno Scheele.* diff --git a/Red-Black Tree/RedBlackTree.playground/Contents.swift b/Red-Black Tree/RedBlackTree.playground/Contents.swift index f14f18e58..d1133bc98 100644 --- a/Red-Black Tree/RedBlackTree.playground/Contents.swift +++ b/Red-Black Tree/RedBlackTree.playground/Contents.swift @@ -2,11 +2,6 @@ // Test for the RedBlackTree implementation provided in the Source folder of this Playground import Foundation -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - let redBlackTree = RedBlackTree() let randomMax = Double(0x10000000) diff --git a/Red-Black Tree/RedBlackTree.playground/Sources/RedBlackTree.swift b/Red-Black Tree/RedBlackTree.playground/Sources/RedBlackTree.swift index fc0411b42..9b067de55 100644 --- a/Red-Black Tree/RedBlackTree.playground/Sources/RedBlackTree.swift +++ b/Red-Black Tree/RedBlackTree.playground/Sources/RedBlackTree.swift @@ -96,6 +96,10 @@ public class RBTreeNode: Equatable { var uncle: RBNode? { return parent?.sibling } + + public func getKey() -> T? { + return key + } } // MARK: - RedBlackTree @@ -160,31 +164,52 @@ extension RBTreeNode { } } -// MARK: - Finding a nodes successor +// MARK: - Finding a nodes successor and predecessor extension RBTreeNode { - /* - * Returns the inorder successor node of a node - * The successor is a node with the next larger key value of the current node - */ - public func getSuccessor() -> RBNode? { - // If node has right child: successor min of this right tree - if let rightChild = self.rightChild { - if !rightChild.isNullLeaf { - return rightChild.minimum() - } - } - // Else go upward until node left child - var currentNode = self - var parent = currentNode.parent - while currentNode.isRightChild { - if let parent = parent { - currentNode = parent - } - parent = currentNode.parent - } - return parent - } + /* + * Returns the inorder successor node of a node + * The successor is a node with the next larger key value of the current node + */ + public func getSuccessor() -> RBNode? { + // If node has right child: successor min of this right tree + if let rightChild = self.rightChild { + if !rightChild.isNullLeaf { + return rightChild.minimum() + } + } + // Else go upward until node left child + var currentNode = self + var parent = currentNode.parent + while currentNode.isRightChild { + if let parent = parent { + currentNode = parent + } + parent = currentNode.parent + } + return parent + } + + /* + * Returns the inorder predecessor node of a node + * The predecessor is a node with the next smaller key value of the current node + */ + public func getPredecessor() -> RBNode? { + // if node has left child: predecessor is min of this left tree + if let leftChild = leftChild, !leftChild.isNullLeaf { + return leftChild.maximum() + } + // else go upward while node is left child + var currentNode = self + var parent = currentNode.parent + while currentNode.isLeftChild { + if let parent = parent { + currentNode = parent + } + parent = currentNode.parent + } + return parent + } } // MARK: - Searching diff --git a/Red-Black Tree/RedBlackTree.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Red-Black Tree/RedBlackTree.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Red-Black Tree/RedBlackTree.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Ring Buffer/RingBuffer.playground/Contents.swift b/Ring Buffer/RingBuffer.playground/Contents.swift index f9ddb9c99..adcf146b4 100644 --- a/Ring Buffer/RingBuffer.playground/Contents.swift +++ b/Ring Buffer/RingBuffer.playground/Contents.swift @@ -1,10 +1,5 @@ //: Playground - noun: a place where people can play -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - public struct RingBuffer { fileprivate var array: [T?] fileprivate var readIndex = 0 diff --git a/Run-Length Encoding/RLE.playground/Contents.swift b/Run-Length Encoding/RLE.playground/Contents.swift index fc2d82505..8d6a0a403 100644 --- a/Run-Length Encoding/RLE.playground/Contents.swift +++ b/Run-Length Encoding/RLE.playground/Contents.swift @@ -1,10 +1,5 @@ //: Playground - noun: a place where people can play -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - import Foundation let originalString = "aaaaabbbcdeeeeeeef" @@ -64,7 +59,7 @@ func testBufferWithoutSpans() -> Bool { // data ends up being longer. var bytes: [UInt8] = [] for i in 0..<1024 { - bytes.append(UInt8(i%256)) + bytes.append(UInt8(i % 256)) } return encodeAndDecode(bytes) } diff --git a/Selection Sampling/README.markdown b/Selection Sampling/README.markdown index ff80791e1..741f592f4 100644 --- a/Selection Sampling/README.markdown +++ b/Selection Sampling/README.markdown @@ -193,7 +193,7 @@ print(output.count) The performance of this second algorithm is **O(n)** as it may require a pass through the entire input array. -> **Note:** If `k > n/2`, then it's more efficient to do it the other way around and choose `k` items to remove. +> **Note:** If `k > n/2`, then it's more efficient to do it the other way around and choose `a.count - k` items to remove. Based on code from Algorithm Alley, Dr. Dobb's Magazine, October 1993. diff --git a/Selection Sort/README.markdown b/Selection Sort/README.markdown index cbcf5d352..b958da927 100644 --- a/Selection Sort/README.markdown +++ b/Selection Sort/README.markdown @@ -74,7 +74,7 @@ func selectionSort(_ array: [Int]) -> [Int] { } if x != lowest { // 5 - swap(&a[x], &a[lowest]) + a.swapAt(x, lowest) } } return a diff --git a/Selection Sort/SelectionSort.playground/Contents.swift b/Selection Sort/SelectionSort.playground/Contents.swift index e0bfdf259..a552c9edb 100644 --- a/Selection Sort/SelectionSort.playground/Contents.swift +++ b/Selection Sort/SelectionSort.playground/Contents.swift @@ -1,10 +1,6 @@ //: Playground - noun: a place where people can play -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - let list = [ 10, -1, 3, 9, 2, 27, 8, 5, 1, 3, 0, 26 ] +selectionSort(list) selectionSort(list, <) selectionSort(list, >) diff --git a/Selection Sort/SelectionSort.playground/Sources/SelectionSort.swift b/Selection Sort/SelectionSort.playground/Sources/SelectionSort.swift index 3015fde9b..e157319b4 100644 --- a/Selection Sort/SelectionSort.playground/Sources/SelectionSort.swift +++ b/Selection Sort/SelectionSort.playground/Sources/SelectionSort.swift @@ -1,21 +1,35 @@ -public func selectionSort(_ array: [T], _ isOrderedBefore: (T, T) -> Bool) -> [T] { - guard array.count > 1 else { return array } - - var a = array - for x in 0 ..< a.count - 1 { - - // Find the lowest value in the rest of the array. - var lowest = x - for y in x + 1 ..< a.count { - if isOrderedBefore(a[y], a[lowest]) { - lowest = y - } - } +/// Performs the Selection sort algorithm on a array +/// +/// - Parameter array: array of elements that conform to the Comparable protocol +/// - Returns: an array in ascending order +public func selectionSort(_ array: [T]) -> [T] { + return selectionSort(array, <) +} - // Swap the lowest value with the current array index. - if x != lowest { - a.swapAt(x, lowest) +/// Performs the Selection sort algorithm on a array using the provided comparisson method +/// +/// - Parameters: +/// - array: array of elements that conform to the Comparable protocol +/// - isLowerThan: returns true if the two provided elements are in the correct order +/// - Returns: a sorted array +public func selectionSort(_ array: [T], _ isLowerThan: (T, T) -> Bool) -> [T] { + guard array.count > 1 else { return array } + + var a = array + for x in 0 ..< a.count - 1 { + + // Find the lowest value in the rest of the array. + var lowest = x + for y in x + 1 ..< a.count { + if isLowerThan(a[y], a[lowest]) { + lowest = y + } + } + + // Swap the lowest value with the current array index. + if x != lowest { + a.swapAt(x, lowest) + } } - } - return a + return a } diff --git a/Selection Sort/SelectionSort.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Selection Sort/SelectionSort.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Selection Sort/SelectionSort.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Set Cover (Unweighted)/SetCover.playground/Contents.swift b/Set Cover (Unweighted)/SetCover.playground/Contents.swift index d61b9c84a..065f5fd4d 100644 --- a/Set Cover (Unweighted)/SetCover.playground/Contents.swift +++ b/Set Cover (Unweighted)/SetCover.playground/Contents.swift @@ -1,10 +1,5 @@ // SetCover -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - let universe1 = Set(1...7) let array1 = randomArrayOfSets(covering: universe1) let cover1 = universe1.cover(within: array1) @@ -36,3 +31,5 @@ let emptySet = Set() let coverTest1 = emptySet.cover(within: array1) let coverTest2 = universe1.cover(within: Array>()) let coverTest3 = emptySet.cover(within: Array>()) + + diff --git a/Set Cover (Unweighted)/SetCover.playground/Sources/RandomArrayOfSets.swift b/Set Cover (Unweighted)/SetCover.playground/Sources/RandomArrayOfSets.swift index 9454c6383..ac4ff063e 100644 --- a/Set Cover (Unweighted)/SetCover.playground/Sources/RandomArrayOfSets.swift +++ b/Set Cover (Unweighted)/SetCover.playground/Sources/RandomArrayOfSets.swift @@ -14,10 +14,10 @@ public func randomArrayOfSets(covering universe: Set, while true { var generatedSet = Set() - let targetSetSize = Int(arc4random_uniform(UInt32(maxSetSize)) + 1) + let targetSetSize = Int.random(in: 0...maxSetSize) + 1 while true { - let randomUniverseIndex = Int(arc4random_uniform(UInt32(universe.count))) + let randomUniverseIndex = Int.random(in: 0...universe.count) for (setIndex, value) in universe.enumerated() { if setIndex == randomUniverseIndex { generatedSet.insert(value) diff --git a/Shuffle/README.markdown b/Shuffle/README.markdown index ced138572..5a2a07a98 100644 --- a/Shuffle/README.markdown +++ b/Shuffle/README.markdown @@ -33,8 +33,6 @@ You should see three different arrangements -- or [permutations](../Combinatoric This shuffle works *in place*, it modifies the contents of the original array. The algorithm works by creating a new array, `temp`, that is initially empty. Then we randomly choose an element from the original array and append it to `temp`, until the original array is empty. Finally, the temporary array is copied back into the original one. -> **Note:** `random()` is a helper function that returns a random integer between 0 and the given maximum. - This code works just fine but it's not very efficient. Removing an element from an array is an **O(n)** operation and we perform this **n** times, making the total algorithm **O(n^2)**. We can do better! ## The Fisher-Yates / Knuth shuffle @@ -45,7 +43,7 @@ Here is a much improved version of the shuffle algorithm: extension Array { public mutating func shuffle() { for i in stride(from: count - 1, through: 1, by: -1) { - let j = random(i + 1) + let j = Int.random(in: 0...i) if i != j { swap(&self[i], &self[j]) } @@ -56,8 +54,6 @@ extension Array { Again, this picks objects at random. In the naive version we placed those objects into a new temporary array so we could keep track of which objects were already shuffled and which still remained to be done. In this improved algorithm, however, we'll move the shuffled objects to the end of the original array. -> **Note**: When you write `random(x)`, the largest number it will return is `x - 1`. We want to have `j <= i`, not `j < i`, so the largest number from the random number generator needs to be `i`, not `i - 1`. That's why we do `random(i + 1)`. If we didn't add that 1 to compensate, it would make some shuffle orders more likely to occur than others. - Let's walk through the example. We have the array: [ "a", "b", "c", "d", "e", "f", "g" ] @@ -99,10 +95,9 @@ Here is the code: public func shuffledArray(_ n: Int) -> [Int] { var a = [Int](repeating: 0, count: n) for i in 0..=4.0) -print("Hello, Swift 4!") -#endif - import Foundation var list = [ "a", "b", "c", "d", "e", "f", "g" ] diff --git a/Shuffle/Shuffle.playground/Sources/Shuffle.swift b/Shuffle/Shuffle.playground/Sources/Shuffle.swift index 553832d43..500d3c245 100644 --- a/Shuffle/Shuffle.playground/Sources/Shuffle.swift +++ b/Shuffle/Shuffle.playground/Sources/Shuffle.swift @@ -1,10 +1,5 @@ import Foundation -/* Returns a random integer between 0 and n-1. */ -public func random(_ n: Int) -> Int { - return Int(arc4random_uniform(UInt32(n))) -} - extension Array { /* Randomly shuffles the array in-place @@ -13,7 +8,7 @@ extension Array { */ public mutating func shuffle() { for i in (1...count-1).reversed() { - let j = random(i + 1) + let j = Int.random(in: 0...i) if i != j { let t = self[i] self[i] = self[j] @@ -29,7 +24,7 @@ extension Array { public func shuffledArray(_ n: Int) -> [Int] { var a = Array(repeating: 0, count: n) for i in 0.. [Int] { var a = Array(repeating: 0, count: n) for i in 0..=4.0) -print("Hello, Swift 4!") -#endif - internal enum OperatorAssociativity { case leftAssociative case rightAssociative diff --git a/Shunting Yard/ShuntingYard.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Shunting Yard/ShuntingYard.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Shunting Yard/ShuntingYard.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Simulated annealing/README.md b/Simulated annealing/README.md new file mode 100644 index 000000000..3a36464d7 --- /dev/null +++ b/Simulated annealing/README.md @@ -0,0 +1,36 @@ +# Simulated annealing + +Simulated Annealing is a nature inspired global optimization technique and a metaheuristic to approximate global maxima in a (often discrete)large search space. The name comes from the process of annealing in metallurgy where a material is heated and cooled down under controlled conditions in order to improve its strength and durabilility. The objective is to find a minimum cost solution in the search space by exploiting properties of a thermodynamic system. +Unlike hill climbing techniques which usually gets stuck in a local maxima ( downward moves are not allowed ), simulated annealing can escape local maxima. The interesting property of simulated annealing is that probability of allowing downward moves is high at the high temperatures and gradually reduced as it cools down. In other words, high temperature relaxes the acceptance criteria for the search space and triggers chaotic behavior of acceptance function in the algorithm (e.x initial/high temperature stages) which should make it possible to escape from local maxima and cooler temperatures narrows it and focuses on improvements. + +Pseucocode + + Input: initial, temperature, coolingRate, acceptance + Output: Sbest + Scurrent <- CreateInitialSolution(initial) + Sbest <- Scurrent + while temperature is not minimum: + Snew <- FindNewSolution(Scurrent) + if acceptance(Energy(Scurrent), Energy(Snew), temperature) > Rand(): + Scurrent = Snew + if Energy(Scurrent) < Energy(Sbest): + Sbest = Scurrent + temperature = temperature * (1-coolingRate) + +Common acceptance criteria : + + P(accept) <- exp((e-ne)/T) where + e is the current energy ( current solution ), + ne is new energy ( new solution ), + T is current temperature. + + +We use this algorithm to solve a Travelling salesman problem instance with 20 cities. The code is in `simann_example.swift` + +#See also + +[Simulated annealing on Wikipedia](https://en.wikipedia.org/wiki/Simulated_annealing) + +[Travelling salesman problem](https://en.wikipedia.org/wiki/Travelling_salesman_problem) + +Written for Swift Algorithm Club by [Mike Taghavi](https://github.com/mitghi) diff --git a/Simulated annealing/simann.swift b/Simulated annealing/simann.swift new file mode 100644 index 000000000..5dc624d04 --- /dev/null +++ b/Simulated annealing/simann.swift @@ -0,0 +1,95 @@ +// The MIT License (MIT) +// Copyright (c) 2017 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. + +#if os(OSX) + import Foundation +#elseif os(Linux) + import Glibc +#endif + +protocol Clonable { + init(current: Self) +} + +// MARK: - create a clone from instance + +extension Clonable { + func clone() -> Self { + return Self.init(current: self) + } +} + +protocol SAObject: Clonable { + var count: Int { get } + func randSwap(a: Int, b: Int) + func currentEnergy() -> Double + func shuffle() +} + +// MARK: - create a new copy of elements + +extension Array where Element: Clonable { + func clone() -> Array { + var newArray = Array() + for elem in self { + newArray.append(elem.clone()) + } + + return newArray + } +} + +typealias AcceptanceFunc = (Double, Double, Double) -> Double + +func SimulatedAnnealing(initial: T, temperature: Double, coolingRate: Double, acceptance: AcceptanceFunc) -> T { + // Step 1: + // Calculate the initial feasible solution based on a random permutation. + // Set best and current solutions to initial solution + + var temp: Double = temperature + var currentSolution = initial.clone() + currentSolution.shuffle() + var bestSolution = currentSolution.clone() + + // Step 2: + // Repeat while the system is still hot + // Randomly modify the current solution by swapping its elements + // Randomly decide if the new solution ( neighbor ) is acceptable and set current solution accordingly + // Update the best solution *iff* it had improved ( lower energy = improvement ) + // Reduce temperature + + while temp > 1 { + let newSolution: T = currentSolution.clone() + let pos1: Int = Int.random(in: 0 ..< newSolution.count) + let pos2: Int = Int.random(in: 0 ..< newSolution.count) + newSolution.randSwap(a: pos1, b: pos2) + let currentEnergy: Double = currentSolution.currentEnergy() + let newEnergy: Double = newSolution.currentEnergy() + + if acceptance(currentEnergy, newEnergy, temp) > Double.random(in: 0 ..< 1) { + currentSolution = newSolution.clone() + } + if currentSolution.currentEnergy() < bestSolution.currentEnergy() { + bestSolution = currentSolution.clone() + } + + temp *= 1-coolingRate + } + + return bestSolution +} diff --git a/Simulated annealing/simann_example.swift b/Simulated annealing/simann_example.swift new file mode 100644 index 000000000..e7933c89a --- /dev/null +++ b/Simulated annealing/simann_example.swift @@ -0,0 +1,206 @@ +// The MIT License (MIT) +// Copyright (c) 2017 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. + +#if os(OSX) + import Foundation + import Cocoa +#elseif os(Linux) + import Glibc +#endif + +protocol Clonable { + init(current: Self) +} + +extension Clonable { + func clone() -> Self { + return Self.init(current: self) + } +} + +protocol SAObject: Clonable { + var count: Int { get } + func randSwap(a: Int, b: Int) + func currentEnergy() -> Double + func shuffle() +} + +// MARK: - create a new copy of elements + +extension Array where Element: Clonable { + func clone() -> Array { + var newArray = Array() + for elem in self { + newArray.append(elem.clone()) + } + + return newArray + } +} + +typealias Points = [Point] +typealias AcceptanceFunc = (Double, Double, Double) -> Double + +class Point: Clonable { + var x: Int + var y: Int + + init(x: Int, y: Int) { + self.x = x + self.y = y + } + + required init(current: Point){ + self.x = current.x + self.y = current.y + } +} + +// MARK: - string representation + +extension Point: CustomStringConvertible { + public var description: String { + return "Point(\(x), \(y))" + } +} + +// MARK: - return distance between two points using operator '<->' + +infix operator <->: AdditionPrecedence +extension Point { + static func <-> (left: Point, right: Point) -> Double { + let xDistance = (left.x - right.x) + let yDistance = (left.y - right.y) + + return Double((xDistance * xDistance) + (yDistance * yDistance)).squareRoot() + } +} + + +class Tour: SAObject { + var tour: Points + var energy: Double = 0.0 + var count: Int { + get { + return self.tour.count + } + } + + init(points: Points){ + self.tour = points.clone() + } + + required init(current: Tour) { + self.tour = current.tour.clone() + } +} + +// MARK: - calculate current tour distance ( energy ). + +extension Tour { + func randSwap(a: Int, b: Int) -> Void { + let (cpos1, cpos2) = (self[a], self[b]) + self[a] = cpos2 + self[b] = cpos1 + } + + func currentEnergy() -> Double { + if self.energy == 0 { + var tourEnergy: Double = 0.0 + for i in 0..destCity + tourEnergy = tourEnergy + e + + } + self.energy = tourEnergy + } + return self.energy + } + + func shuffle() { + self.tour.shuffle() + } +} + +// MARK: - subscript to manipulate elements of Tour. + +extension Tour { + subscript(index: Int) -> Point { + get { + return self.tour[index] + } + set(newValue) { + self.tour[index] = newValue + } + } +} + +func SimulatedAnnealing(initial: T, temperature: Double, coolingRate: Double, acceptance: AcceptanceFunc) -> T { + var temp: Double = temperature + var currentSolution = initial.clone() + currentSolution.shuffle() + var bestSolution = currentSolution.clone() + print("Initial solution: ", bestSolution.currentEnergy()) + + while temp > 1 { + let newSolution: T = currentSolution.clone() + let pos1: Int = Int.random(in: 0 ..< newSolution.count) + let pos2: Int = Int.random(in: 0 ..< newSolution.count) + newSolution.randSwap(a: pos1, b: pos2) + let currentEnergy: Double = currentSolution.currentEnergy() + let newEnergy: Double = newSolution.currentEnergy() + + if acceptance(currentEnergy, newEnergy, temp) > Double.random(in: 0 ..< 1) { + currentSolution = newSolution.clone() + } + if currentSolution.currentEnergy() < bestSolution.currentEnergy() { + bestSolution = currentSolution.clone() + } + + temp *= 1-coolingRate + } + + print("Best solution: ", bestSolution.currentEnergy()) + return bestSolution +} + +let points: [Point] = [ + (60 , 200), (180, 200), (80 , 180), (140, 180), + (20 , 160), (100, 160), (200, 160), (140, 140), + (40 , 120), (100, 120), (180, 100), (60 , 80) , + (120, 80) , (180, 60) , (20 , 40) , (100, 40) , + (200, 40) , (20 , 20) , (60 , 20) , (160, 20) , + ].map{ Point(x: $0.0, y: $0.1) } + +let acceptance : AcceptanceFunc = { + (e: Double, ne: Double, te: Double) -> Double in + if ne < e { + return 1.0 + } + return exp((e - ne) / te) +} + +let result: Tour = SimulatedAnnealing(initial : Tour(points: points), + temperature : 100000.0, + coolingRate : 0.003, + acceptance : acceptance) diff --git a/Skip-List/SkipList.playground/Contents.swift b/Skip-List/SkipList.playground/Contents.swift index 1b4847c0e..55b9d3a23 100644 --- a/Skip-List/SkipList.playground/Contents.swift +++ b/Skip-List/SkipList.playground/Contents.swift @@ -5,3 +5,16 @@ print("Hello, Swift 4!") // SkipList is ready for Swift 4. // TODO: Add Test + +let k = SkipList() +k.insert(key: 10, data: "10") +k.insert(key: 12, data: "12") +k.insert(key: 13, data: "13") +k.insert(key: 20, data: "20") +k.insert(key: 24, data: "24") + +if let value = k.get(key: 20) { + print(value) +} else { + print("not found!") +} diff --git a/Skip-List/SkipList.playground/Sources/SkipList.swift b/Skip-List/SkipList.playground/Sources/SkipList.swift index f626da0f8..186286977 100644 --- a/Skip-List/SkipList.playground/Sources/SkipList.swift +++ b/Skip-List/SkipList.playground/Sources/SkipList.swift @@ -212,7 +212,7 @@ extension SkipList { } } - func insert(key: Key, data: Payload) { + public func insert(key: Key, data: Payload) { if head != nil { if let node = findNode(key: key) { // replace, in case of key already exists. diff --git a/Skip-List/SkipList.swift b/Skip-List/SkipList.swift index fc635d0f2..8a1959f70 100644 --- a/Skip-List/SkipList.swift +++ b/Skip-List/SkipList.swift @@ -212,7 +212,7 @@ extension SkipList { } } - func insert(key: Key, data: Payload) { + public func insert(key: Key, data: Payload) { if head != nil { if let node = findNode(key: key) { // replace, in case of key already exists. diff --git a/Slow Sort/SlowSort.playground/Contents.swift b/Slow Sort/SlowSort.playground/Contents.swift index 95bf80739..54c1e61ea 100644 --- a/Slow Sort/SlowSort.playground/Contents.swift +++ b/Slow Sort/SlowSort.playground/Contents.swift @@ -1,9 +1,4 @@ -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - var numberList = [1, 12, 9, 17, 13, 12] -slowsort(0, numberList.count-1, &numberList) +slowSort(0, numberList.count-1, &numberList) print(numberList) diff --git a/Slow Sort/SlowSort.swift b/Slow Sort/SlowSort.swift index 9f264a057..968f6841b 100644 --- a/Slow Sort/SlowSort.swift +++ b/Slow Sort/SlowSort.swift @@ -1,24 +1,20 @@ // // SlowSort.swift -// +// // // Created by Pope Lukas Schramm (Dabendorf Orthodox Religion) on 16-07-16. // // -import Foundation - -public func slowsort(_ i: Int, _ j: Int, _ numberList: inout [Int]) { - if i>=j { - return - } +func slowSort(_ i: Int, _ j: Int, _ numberList: inout [Int]) { + guard if i < j else { return } let m = (i+j)/2 - slowsort(i, m, &numberList) - slowsort(m+1, j, &numberList) + slowSort(i, m, &numberList) + slowSort(m+1, j, &numberList) if numberList[j] < numberList[m] { let temp = numberList[j] numberList[j] = numberList[m] numberList[m] = temp } - slowsort(i, j-1, &numberList) + slowSort(i, j-1, &numberList) } diff --git a/Sorted Set/README.markdown b/Sorted Set/README.markdown new file mode 100644 index 000000000..a8b5ad1ea --- /dev/null +++ b/Sorted Set/README.markdown @@ -0,0 +1,334 @@ +# Sorted Set + +## Sorted Array Version + +An Sorted Set is a collection of unique items in sorted order. Items are usually sorted from least to greatest. + +The Sorted Set data type is a hybrid of: + +- a [Set](https://en.wikipedia.org/wiki/Set_%28mathematics%29), a collection of unique items where the order does not matter, and +- a [Sequence](https://en.wikipedia.org/wiki/Sequence), an sorted list of items where each item may appear more than once. + +It's important to keep in mind that two items can have the same *value* but still may not be equal. For example, we could define "a" and "z" to have the same value (their lengths), but clearly "a" != "z". + +## Why use an sorted set? + +Sorted Sets should be considered when you need to keep your collection sorted at all times, and you do lookups on the collection much more frequently than inserting or deleting items. Many of the lookup operations for an Sorted Set are **O(1)**. + +A good example would be keeping track of the rankings of players in a scoreboard (see example 2 below). + +#### These are sorted sets + +A set of integers: + + [1, 2, 3, 6, 8, 10, 1000] + +A set of strings: + + ["a", "is", "set", "this"] + +The "value" of these strings could be their text content, but also for example their length. + +#### These are not sorted sets + +This set violates the property of uniqueness: + + [1, 1, 2, 3, 5, 8] + +This set violates the sorted property: + + [1, 11, 2, 3] + +## The code + +We'll start by creating our internal representation for the Sorted Set. Since the idea of a set is similar to that of an array, we will use an array to represent our set. Furthermore, since we'll need to keep the set sorted, we need to be able to compare the individual elements. Thus, any type must conform to the [Comparable Protocol](https://developer.apple.com/library/watchos/documentation/Swift/Reference/Swift_Comparable_Protocol/index.html). + +```swift +public struct SortedSet { + private var internalSet = [T]() + + // Returns the number of elements in the SortedSet. + public var count: Int { + return internalSet.count + } + ... +``` + +Lets take a look at the `insert()` function first. This first checks if the item already exists in the collection. If so, it returns and does not insert the item. Otherwise, it will insert the item through straightforward iteration. + +```swift + public mutating func insert(_ item: T){ + if exists(item) { + return // don't add an item if it already exists + } + + // Insert new the item just before the one that is larger. + for i in 0.. item { + internalSet.insert(item, at: i) + return + } + } + + // Append to the back if the new item is greater than any other in the set. + internalSet.append(item) + } +``` + +As we'll see later on, checking if the item is already in the set has an efficiency of **O(log(n) + k)** where **k** is the number of items with the same value as the item we are inserting. + +To insert the new item, the `for` loop starts from the beginning of the array, and checks to see if each item is larger than the item we want to insert. Once we find such an item, we insert the new one into its place. This shifts the rest of the array over to the right by 1 position. This loop is at worst **O(n)**. + +The total performance of the `insert()` function is therefore **O(n)**. + +Next up is the `remove()` function: + +```swift + public mutating func remove(_ item: T) { + if let index = index(of: item) { + internalSet.remove(at: index) + } + } +``` + +First this checks if the item exists and then removes it from the array. Because of the `removeAtIndex()` function, the efficiency for remove is **O(n)**. + +The next function is `indexOf()`, which takes in an object of type `T` and returns the index of the corresponding item if it is in the set, or `nil` if it is not. Since our set is sorted, we can use a binary search to quickly search for the item. + +```swift + public func index(of item: T) -> Int? { + var leftBound = 0 + var rightBound = count - 1 + + while leftBound <= rightBound { + let mid = leftBound + ((rightBound - leftBound) / 2) + + if internalSet[mid] > item { + rightBound = mid - 1 + } else if internalSet[mid] < item { + leftBound = mid + 1 + } else if internalSet[mid] == item { + return mid + } else { + // see below + } + } + return nil + } +``` + +> **Note:** If you are not familiar with the concept of binary search, we have an [article that explains all about it](../Binary%20Search). + +However, there is an important issue to deal with here. Recall that two objects can be unequal yet still have the same "value" for the purposes of comparing them. Since a set can contain multiple items with the same value, it is important to check that the binary search has landed on the correct item. + +For example, consider this sorted set of `Player` objects. Each `Player` has a name and a number of points: + + [ ("Bill", 50), ("Ada", 50), ("Jony", 50), ("Steve", 200), ("Jean-Louis", 500), ("Woz", 1000) ] + +We want the set to be sorted by points, from low to high. Multiple players can have the same number of points. The name of the player is not important for this ordering. However, the name *is* important for retrieving the correct item. + +Let's say we do `indexOf(bill)` where `bill` is player object `("Bill", 50)`. If we did a traditional binary search we'd land on index 2, which is the object `("Jony", 50)`. The value 50 matches, but it's not the object we're looking for! + +Therefore, we also need to check the items with the same value to the right and left of the midpoint. The code to check the left and right side looks like this: + +```swift + // Check to the right. + for j in mid.stride(to: count - 1, by: 1) { + if internalSet[j + 1] == item { + return j + 1 + } else if internalSet[j] < internalSet[j + 1] { + break + } + } + + // Check to the left. + for j in mid.stride(to: 0, by: -1) { + if internalSet[j - 1] == item { + return j - 1 + } else if internalSet[j] > internalSet[j - 1] { + break + } + } + + return nil +``` + +These loops start at the current `mid` value and then look at the neighboring values until we've found the correct object. + +The combined runtime for `indexOf()` is **O(log(n) + k)** where **n** is the length of the set, and **k** is the number of items with the same *value* as the one that is being searched for. + +Since the set is sorted, the following operations are all **O(1)**: + +```swift + // Returns the 'maximum' or 'largest' value in the set. + public func max() -> T? { + return count == 0 ? nil : internalSet[count - 1] + } + + // Returns the 'minimum' or 'smallest' value in the set. + public func min() -> T? { + return count == 0 ? nil : internalSet[0] + } + + // Returns the k-th largest element in the set, if k is in the range + // [1, count]. Returns nil otherwise. + public func kLargest(_ k: Int) -> T? { + return k > count || k <= 0 ? nil : internalSet[count - k] + } + + // Returns the k-th smallest element in the set, if k is in the range + // [1, count]. Returns nil otherwise. + public func kSmallest(_ k: Int) -> T? { + return k > count || k <= 0 ? nil : internalSet[k - 1] + } +``` + +## Examples + +Below are a few examples that can be found in the playground file. + +### Example 1 + +Here we create a set with random Integers. Printing the largest/smallest 5 numbers in the set is fairly easy. + +```swift +// Example 1 with type Int +var mySet = SortedSet() + +// Insert random numbers into the set +for _ in 0..<50 { + mySet.insert(randomNum(50, max: 500)) +} + +print(mySet) + +print(mySet.max()) +print(mySet.min()) + +// Print the 5 largest values +for k in 1...5 { + print(mySet.kLargest(k)) +} + +// Print the 5 lowest values +for k in 1...5 { + print(mySet.kSmallest(k)) +} +``` + +### Example 2 + +In this example we take a look at something a bit more interesting. We define a `Player` struct as follows: + +```swift +public struct Player: Comparable { + public var name: String + public var points: Int +} +``` + +The `Player` also gets its own `==` and `<` operators. The `<` operator is used to determine the sort order of the set, while `==` determines whether two objects are really equal. + +Note that `==` compares both the name and the points: + +```swifr +func ==(x: Player, y: Player) -> Bool { + return x.name == y.name && x.points == y.points +} +``` + +But `<` only compares the points: + +```swift +func <(x: Player, y: Player) -> Bool { + return x.points < y.points +} +``` + +Therefore, two `Player`s can each have the same value (the number of points), but are not guaranteed to be equal (they can have different names). + +We create a new set and insert 20 random players. The `Player()` constructor gives each player a random name and score: + +```swift +var playerSet = SortedSet() + +// Populate the set with random players. +for _ in 0..<20 { + playerSet.insert(Player()) +} +``` + +Insert another player: + +```swift +var anotherPlayer = Player() +playerSet.insert(anotherPlayer) +``` + +Now we use the `indexOf()` function to find out what rank `anotherPlayer` is. + +```swift +let level = playerSet.count - playerSet.indexOf(anotherPlayer)! +print("\(anotherPlayer.name) is ranked at level \(level) with \(anotherPlayer.points) points") +``` + +### Example 3 + +The final example demonstrates the need to look for the right item even after the binary search has completed. + +We insert 9 players into the set: + +```swift +var repeatedSet = SortedSet() + +repeatedSet.insert(Player(name: "Player 1", points: 100)) +repeatedSet.insert(Player(name: "Player 2", points: 100)) +repeatedSet.insert(Player(name: "Player 3", points: 100)) +repeatedSet.insert(Player(name: "Player 4", points: 100)) +repeatedSet.insert(Player(name: "Player 5", points: 100)) +repeatedSet.insert(Player(name: "Player 6", points: 50)) +repeatedSet.insert(Player(name: "Player 7", points: 200)) +repeatedSet.insert(Player(name: "Player 8", points: 250)) +repeatedSet.insert(Player(name: "Player 9", points: 25)) +``` + +Notice how several of these players have the same value of 100 points. + +The set looks something like this: + + [Player 9, Player 6, Player 1, Player 2, Player 3, Player 4, Player 5, Player 7, Player 8] + +The next line looks for `Player 2`: + +```swift +print(repeatedSet.index(of: Player(name: "Player 2", points: 100))) +``` + +After the binary search finishes, the value of `mid` is at index 5: + + [Player 9, Player 6, Player 1, Player 2, Player 3, Player 4, Player 5, Player 7, Player 8] + mid + +However, this is not `Player 2`. Both `Player 4` and `Player 2` have the same points, but a different name. The binary search only looked at the points, not the name. + +But we do know that `Player 2` must be either to the immediate left or the right of `Player 4`, so we check both sides of `mid`. We only need to look at the objects with the same value as `Player 4`. The others are replaced by `X`: + + [X, X, Player 1, Player 2, Player 3, Player 4, Player 5, X, X] + mid + +The code then first checks on the right of `mid` (where the `*` is): + + [X, X, Player 1, Player 2, Player 3, Player 4, Player 5, X, X] + mid * + +The right side did not contain the item, so we look at the left side: + + [X, X, Player 1, Player 2, Player 3, Player 4, Player 5, X, X] + * mid + + [X, X, Player 1, Player 2, Player 3, Player 4, Player 5, X, X] + * mid + +Finally, we've found `Player 2`, and return index 3. + +*Written By Zain Humayun* diff --git a/Ordered Set/OrderedSet.playground/Pages/Example 1.xcplaygroundpage/Contents.swift b/Sorted Set/SortedSet.playground/Pages/Example 1.xcplaygroundpage/Contents.swift similarity index 79% rename from Ordered Set/OrderedSet.playground/Pages/Example 1.xcplaygroundpage/Contents.swift rename to Sorted Set/SortedSet.playground/Pages/Example 1.xcplaygroundpage/Contents.swift index c12dadf0f..c81f4d915 100644 --- a/Ordered Set/OrderedSet.playground/Pages/Example 1.xcplaygroundpage/Contents.swift +++ b/Sorted Set/SortedSet.playground/Pages/Example 1.xcplaygroundpage/Contents.swift @@ -1,11 +1,6 @@ -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - //: # Example 1 with type Int -var mySet = OrderedSet() +var mySet = SortedSet() // Insert random numbers into the set for _ in 0..<50 { diff --git a/Ordered Set/OrderedSet.playground/Pages/Example 1.xcplaygroundpage/timeline.xctimeline b/Sorted Set/SortedSet.playground/Pages/Example 1.xcplaygroundpage/timeline.xctimeline similarity index 50% rename from Ordered Set/OrderedSet.playground/Pages/Example 1.xcplaygroundpage/timeline.xctimeline rename to Sorted Set/SortedSet.playground/Pages/Example 1.xcplaygroundpage/timeline.xctimeline index 53114d22e..dffc42643 100644 --- a/Ordered Set/OrderedSet.playground/Pages/Example 1.xcplaygroundpage/timeline.xctimeline +++ b/Sorted Set/SortedSet.playground/Pages/Example 1.xcplaygroundpage/timeline.xctimeline @@ -3,17 +3,17 @@ version = "3.0"> diff --git a/Ordered Set/OrderedSet.playground/Pages/Example 2.xcplaygroundpage/Contents.swift b/Sorted Set/SortedSet.playground/Pages/Example 2.xcplaygroundpage/Contents.swift similarity index 95% rename from Ordered Set/OrderedSet.playground/Pages/Example 2.xcplaygroundpage/Contents.swift rename to Sorted Set/SortedSet.playground/Pages/Example 2.xcplaygroundpage/Contents.swift index c30260fff..2408d35d2 100644 --- a/Ordered Set/OrderedSet.playground/Pages/Example 2.xcplaygroundpage/Contents.swift +++ b/Sorted Set/SortedSet.playground/Pages/Example 2.xcplaygroundpage/Contents.swift @@ -2,7 +2,7 @@ //: # Example 2 with Player objects -var playerSet = OrderedSet() +var playerSet = SortedSet() // Populate the set with random players. for _ in 0..<20 { diff --git a/Ordered Set/OrderedSet.playground/Pages/Example 3.xcplaygroundpage/Contents.swift b/Sorted Set/SortedSet.playground/Pages/Example 3.xcplaygroundpage/Contents.swift similarity index 95% rename from Ordered Set/OrderedSet.playground/Pages/Example 3.xcplaygroundpage/Contents.swift rename to Sorted Set/SortedSet.playground/Pages/Example 3.xcplaygroundpage/Contents.swift index 52f154c05..63edd11d2 100644 --- a/Ordered Set/OrderedSet.playground/Pages/Example 3.xcplaygroundpage/Contents.swift +++ b/Sorted Set/SortedSet.playground/Pages/Example 3.xcplaygroundpage/Contents.swift @@ -2,7 +2,7 @@ //: # Example 3: multiple entries with the same value -var repeatedSet = OrderedSet() +var repeatedSet = SortedSet() repeatedSet.insert(Player(name: "Player 1", points: 100)) repeatedSet.insert(Player(name: "Player 2", points: 100)) diff --git a/Ordered Set/OrderedSet.playground/Sources/Player.swift b/Sorted Set/SortedSet.playground/Sources/Player.swift similarity index 95% rename from Ordered Set/OrderedSet.playground/Sources/Player.swift rename to Sorted Set/SortedSet.playground/Sources/Player.swift index 26835b0a8..0960b2400 100644 --- a/Ordered Set/OrderedSet.playground/Sources/Player.swift +++ b/Sorted Set/SortedSet.playground/Sources/Player.swift @@ -3,12 +3,12 @@ public struct Player: Comparable { public var name: String public var points: Int - + public init() { self.name = String.random() self.points = random(min: 0, max: 5000) } - + public init(name: String, points: Int) { self.name = name self.points = points @@ -31,7 +31,7 @@ public func print(player: Player) { print("Player: \(player.name) | Points: \(player.points)") } -public func print(set: OrderedSet) { +public func print(set: SortedSet) { for i in 0.. String { let base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" var randomString: String = "" - + for _ in 0.. { + private var internalSet = [T]() + + public init() { } + + // Returns the number of elements in the SortedSet. + public var count: Int { + return internalSet.count + } + + // Inserts an item. Performance: O(n) + public mutating func insert(_ item: T) { + if exists(item) { + return // don't add an item if it already exists + } + + // Insert new the item just before the one that is larger. + for i in 0.. item { + internalSet.insert(item, at: i) + return + } + } + + // Append to the back if the new item is greater than any other in the set. + internalSet.append(item) + } + + // Removes an item if it exists. Performance: O(n) + public mutating func remove(_ item: T) { + if let index = index(of: item) { + internalSet.remove(at: index) + } + } + + // Returns true if and only if the item exists somewhere in the set. + public func exists(_ item: T) -> Bool { + return index(of: item) != nil + } + + // Returns the index of an item if it exists, or -1 otherwise. + public func index(of item: T) -> Int? { + var leftBound = 0 + var rightBound = count - 1 + + while leftBound <= rightBound { + let mid = leftBound + ((rightBound - leftBound) / 2) + + if internalSet[mid] > item { + rightBound = mid - 1 + } else if internalSet[mid] < item { + leftBound = mid + 1 + } else if internalSet[mid] == item { + return mid + } else { + // When we get here, we've landed on an item whose value is equal to the + // value of the item we're looking for, but the items themselves are not + // equal. We need to check the items with the same value to the right + // and to the left in order to find an exact match. + + // Check to the right. + for j in stride(from: mid, to: count - 1, by: 1) { + if internalSet[j + 1] == item { + return j + 1 + } else if internalSet[j] < internalSet[j + 1] { + break + } + } + + // Check to the left. + for j in stride(from: mid, to: 0, by: -1) { + if internalSet[j - 1] == item { + return j - 1 + } else if internalSet[j] > internalSet[j - 1] { + break + } + } + return nil + } + } + return nil + } + + // Returns the item at the given index. + // Assertion fails if the index is out of the range of [0, count). + public subscript(index: Int) -> T { + assert(index >= 0 && index < count) + return internalSet[index] + } + + // Returns the 'maximum' or 'largest' value in the set. + public func max() -> T? { + return count == 0 ? nil : internalSet[count - 1] + } + + // Returns the 'minimum' or 'smallest' value in the set. + public func min() -> T? { + return count == 0 ? nil : internalSet[0] + } + + // Returns the k-th largest element in the set, if k is in the range + // [1, count]. Returns nil otherwise. + public func kLargest(_ k: Int) -> T? { + return k > count || k <= 0 ? nil : internalSet[count - k] + } + + // Returns the k-th smallest element in the set, if k is in the range + // [1, count]. Returns nil otherwise. + public func kSmallest(_ k: Int) -> T? { + return k > count || k <= 0 ? nil : internalSet[k - 1] + } +} + diff --git a/Sorted Set/SortedSet.playground/contents.xcplayground b/Sorted Set/SortedSet.playground/contents.xcplayground new file mode 100644 index 000000000..18c02c912 --- /dev/null +++ b/Sorted Set/SortedSet.playground/contents.xcplayground @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/Sorted Set/SortedSet.playground/playground.xcworkspace/contents.xcworkspacedata b/Sorted Set/SortedSet.playground/playground.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/Sorted Set/SortedSet.playground/playground.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Sorted Set/SortedSet.swift b/Sorted Set/SortedSet.swift new file mode 100644 index 000000000..69546da0d --- /dev/null +++ b/Sorted Set/SortedSet.swift @@ -0,0 +1,118 @@ +/* + An Sorted Set is a collection where all items in the set follow an ordering, + usually sorted from 'least' to 'most'. The way you value and compare items + can be user-defined. + */ +public struct SortedSet { + private var internalSet = [T]() + + public init() { } + + // Returns the number of elements in the SortedSet. + public var count: Int { + return internalSet.count + } + + // Inserts an item. Performance: O(n) + public mutating func insert(_ item: T) { + if exists(item) { + return // don't add an item if it already exists + } + + // Insert new the item just before the one that is larger. + for i in 0.. item { + internalSet.insert(item, at: i) + return + } + } + + // Append to the back if the new item is greater than any other in the set. + internalSet.append(item) + } + + // Removes an item if it exists. Performance: O(n) + public mutating func remove(_ item: T) { + if let index = index(of: item) { + internalSet.remove(at: index) + } + } + + // Returns true if and only if the item exists somewhere in the set. + public func exists(_ item: T) -> Bool { + return index(of: item) != nil + } + + // Returns the index of an item if it exists, or -1 otherwise. + public func index(of item: T) -> Int? { + var leftBound = 0 + var rightBound = count - 1 + + while leftBound <= rightBound { + let mid = leftBound + ((rightBound - leftBound) / 2) + + if internalSet[mid] > item { + rightBound = mid - 1 + } else if internalSet[mid] < item { + leftBound = mid + 1 + } else if internalSet[mid] == item { + return mid + } else { + // When we get here, we've landed on an item whose value is equal to the + // value of the item we're looking for, but the items themselves are not + // equal. We need to check the items with the same value to the right + // and to the left in order to find an exact match. + + // Check to the right. + for j in stride(from: mid, to: count - 1, by: 1) { + if internalSet[j + 1] == item { + return j + 1 + } else if internalSet[j] < internalSet[j + 1] { + break + } + } + + // Check to the left. + for j in stride(from: mid, to: 0, by: -1) { + if internalSet[j - 1] == item { + return j - 1 + } else if internalSet[j] > internalSet[j - 1] { + break + } + } + return nil + } + } + return nil + } + + // Returns the item at the given index. + // Assertion fails if the index is out of the range of [0, count). + public subscript(index: Int) -> T { + assert(index >= 0 && index < count) + return internalSet[index] + } + + // Returns the 'maximum' or 'largest' value in the set. + public func max() -> T? { + return count == 0 ? nil : internalSet[count - 1] + } + + // Returns the 'minimum' or 'smallest' value in the set. + public func min() -> T? { + return count == 0 ? nil : internalSet[0] + } + + // Returns the k-th largest element in the set, if k is in the range + // [1, count]. Returns nil otherwise. + public func kLargest(_ k: Int) -> T? { + return k > count || k <= 0 ? nil : internalSet[count - k] + } + + // Returns the k-th smallest element in the set, if k is in the range + // [1, count]. Returns nil otherwise. + public func kSmallest(_ k: Int) -> T? { + return k > count || k <= 0 ? nil : internalSet[k - 1] + } +} + diff --git a/Sparse Table/Images/idempotency.png b/Sparse Table/Images/idempotency.png new file mode 100644 index 000000000..88c8e7a8d Binary files /dev/null and b/Sparse Table/Images/idempotency.png differ diff --git a/Sparse Table/Images/query.png b/Sparse Table/Images/query.png new file mode 100644 index 000000000..fc1c42fb7 Binary files /dev/null and b/Sparse Table/Images/query.png differ diff --git a/Sparse Table/Images/query_example.png b/Sparse Table/Images/query_example.png new file mode 100644 index 000000000..c588828ee Binary files /dev/null and b/Sparse Table/Images/query_example.png differ diff --git a/Sparse Table/Images/recursion.png b/Sparse Table/Images/recursion.png new file mode 100644 index 000000000..6928e372c Binary files /dev/null and b/Sparse Table/Images/recursion.png differ diff --git a/Sparse Table/Images/recursion_examples.png b/Sparse Table/Images/recursion_examples.png new file mode 100644 index 000000000..737b95bf9 Binary files /dev/null and b/Sparse Table/Images/recursion_examples.png differ diff --git a/Sparse Table/Images/structure.png b/Sparse Table/Images/structure.png new file mode 100644 index 000000000..88902df0c Binary files /dev/null and b/Sparse Table/Images/structure.png differ diff --git a/Sparse Table/Images/structure_examples.png b/Sparse Table/Images/structure_examples.png new file mode 100644 index 000000000..69fd57218 Binary files /dev/null and b/Sparse Table/Images/structure_examples.png differ diff --git a/Sparse Table/README.markdown b/Sparse Table/README.markdown new file mode 100644 index 000000000..31bd5a781 --- /dev/null +++ b/Sparse Table/README.markdown @@ -0,0 +1,310 @@ +# Sparse Table + +I'm excited to present **Sparse Tables**. Despite being somewhat niche, Sparse Tables are simple to implement and extremely powerful. + +### The Problem + +Let's suppose: +- we have an array **a** of some type +- we have some associative binary function **f** [\*]. The function can be: min, max, [gcd](../GCD/), boolean AND, boolean OR ... + +*[\*] where **f** is also "idempotent". Don't worry, I'll explain this in a moment.* + +Our task is as follows: + +- Given two indices **l** and **r**, answer a **query** for the interval `[l, r)` by performing `f(a[l], a[l + 1], a[l + 2], ..., a[r - 1])`; taking all the elements in the range and applying **f** to them +- There will be a *huge* number **Q** of these queries to answer ... so we should be able to answer each query *quickly*! + +For example, if we have an array of numbers: + +```swift +var a = [ 20, 3, -1, 101, 14, 29, 5, 61, 99 ] +``` +and our function **f** is the *min* function. + +Then we may be given a query for interval `[3, 8)`. That means we look at the elements: + +``` +101, 14, 29, 5, 61 +``` + +because these are the elements of **a** with indices +that lie in our range `[3, 8)` – elements from index 3 up to, but not including, index 8. +We then we pass all of these numbers into the min function, +which takes the minimum. The answer to the query is `5`, because that's the result of `min(101, 14, 29, 5, 61)`. + +Imagine we have millions of these queries to process. +> - *Query 1*: Find minimum of all elements between 2 and 5 +> - *Query 2*: Find minimum of all elements between 3 and 9 +> - ... +> - *Query 1000000*: Find minimum of all elements between 1 and 4 + +And our array is very large. Here, let's say **Q** = 1000000 and **N** = 500000. Both numbers are *huge*. We want to make sure that we can answer each query really quickly, or else the number of queries will overwhelm us! + +*So that's the problem.* + +The naive solution to this problem is to perform a `for` loop +to compute the answer for each query. However, for very large **Q** and very large **N** this +will be too slow. We can speed up the time to compute the answer by using a data structure called +a **Sparse Table**. You'll notice that so far, our problem is exactly the same as that of the [Segment Tree](https://github.com/raywenderlich/swift-algorithm-club/tree/master/Segment%20Tree) +(assuming you're familiar). However! ... there's one crucial difference between Segment Trees +and Sparse Tables ... and it concerns our choice of **f**. + + +### A small gotcha ... Idempotency + +Suppose we wanted to find the answer to **`[A, D)`**. +And we already know the answer to two ranges **`[A, B)`** and **`[C, D)`**. +And importantly here, ... *these ranges overlap*!! We have **C** < **B**. + +![Overlapping ranges](Images/idempotency.png) + + +So what? Well, for **f** = minimum function, we can take our answers for **`[A, B)`** and **`[C, D)`** +and combine them! +We can just take the minimum of the two answers: `result = min(x1, x2)` ... *voilà!*, we have the minimum for **`[A, D)`**. +It didn't matter that the intervals overlap - we still found the correct minimum. +But now suppose **f** is the addition operation `+`. Ok, so now we're taking sums over ranges. +If we tried the same approach again, it wouldn't work. That is, +if we took our answers for **`[A, B)`** and **`[C, D)`** +and added them together we'd get a wrong answer for **`[A, D)`**. +*Why?* Well, we'd have counted some elements twice because of the overlap. + +Later, we'll see that in order to answer queries, Sparse Tables use this very technique. +They combine answers in the same way as shown above. Unfortunately this means +we have to exclude certain binary operators from being **f**, including `+`, `*`, XOR, ... +because they don't work with this technique. +In order to get the best speed of a Sparse Table, +we need to make sure that the **f** we're using is an **[idempotent](https://en.wikipedia.org/wiki/Idempotence)** binary operator. +Mathematically, these are operators that satisfy `f(x, x) = x` for all possible **x** that could be in **a**. +Practically speaking, these are the only operators that work; allowing us to combine answers from overlapping ranges. +Examples of idempotent **f**'s are min, max, gcd, boolean AND, boolean OR, bitwise AND and bitwise OR. +Note that for Segment Trees, **f** does not have to be idempotent. That's the crucial difference between +Segment Trees and Sparse Tables. + +*Phew!* Now that we've got that out of the way, let's dive in! + +## Structure of a Sparse Table + +Let's use **f** = min and use the array: + +```swift +var a = [ 10, 6, 5, -7, 9, -8, 2, 4, 20 ] +``` + +In this case, the Sparse Table looks like this: + +![Sparse Table](Images/structure.png) + +What's going on here? There seems to be loads of intervals. +*Correct!* Sparse tables are preloaded with the answers for lots of queries `[l, r)`. +Here's the idea. Before we process our **Q** queries, we'll pre-populate our Sparse Table `table` +with answers to loads of queries; +making it act a bit like a *cache*. When we come to answer one of our queries, we can break the query +down into smaller "sub-queries", each having an answer that's already in the cache. +We lookup the cached answers for the sub-queries in +`table` in constant time +and combine the answers together +to give the overall answer to the original query in speedy time. + +The problem is, we can't store the answers for every single possible query that we could ever have ... +or else our table would be too big! After all, our Sparse Table needs to be *sparse*. So what do we do? +We only pick the "best" intervals to store answers for. And as it turns out, the "best" intervals are those +that have a **width that is a power of two**! + +For example, the answer for the query `[10, 18)` is in our table +because the interval width: `18 - 10 = 8 = 2**3` is a power of two (`**` is the [exponentiation operator](https://en.wikipedia.org/wiki/Exponentiation)). +Also, the answer for `[15, 31)` is in our table because its width: `31 - 15 = 16 = 2**4` is again a power of two. +However, the answer for `[1, 6)` is *not* in there because the interval's width: `6 - 1 = 5` is *not* a power of two. +Consequently, we don't store answers for *all* possible intervals that fit inside **a** – +only the ones with a width that is a power of two. +This is true irrespective of where the interval starts within **a**. +We'll gradually see that this approach works and that relatively speaking, it uses very little space. + +A **Sparse Table** is a table where `table[w][l]` contains the answer for `[l, l + 2**w)`. +It has entries `table[w][l]` where: +- **w** tells us our **width** ... the number of elements or the *width* is `2**w` +- **l** tells us the **lower bound** ... it's the starting point of our interval + +Some examples: +- `table[3][0] = -8`: our width is `2**3`, we start at `l = 0` so our query is `[0, 0 + 2**3) = [0, 8)`. + The answer for this query is `min(10, 6, 5, -7, 9, -8, 2, 4, 20) = -8`. +- `table[2][1] = -7`: our width is `2**2`, we start at `l = 1` so our query is `[1, 1 + 2**2) = [1, 5)`. + The answer for this query is `min(6, 5, -7, 9) = -7`. +- `table[1][7] = 4`: our width is `2**1`, we start at `l = 7` so our query is `[7, 7 + 2**1) = [7, 9)`. + The answer for this query is `min(4, 20) = 4`. +- `table[0][8] = 20`: our width is `2**0`, we start at `l = 8` so our query is`[8, 8 + 2**0) = [8, 9)`. + The answer for this query is `min(20) = 20`. + + +![Sparse Table](Images/structure_examples.png) + + +A Sparse Table can be implemented using a [two-dimentional array](../2D%20Array). + +```swift +public class SparseTable { + private var table: [[T]] + + public init(array: [T], function: @escaping (T, T) -> T, defaultT: T) { + table = [[T]](repeating: [T](repeating: defaultT, count: N), count: W) + } + // ... +} +``` + + +## Building a Sparse Table + +To build a Sparse Table, we compute each table entry starting from the bottom-left and moving up towards +the top-right (in accordance with the diagram). +First we'll compute all the intervals for **w** = 0, then compute all the intervals +and for **w** = 1 and so on. We'll continue up until **w** is big enough such that our intervals are can cover at least half the array. +For each **w**, we compute the interval for **l** = 0, 1, 2, 3, ... until we reach **N**. +This is all achieved using a double `for`-`in` loop: + +```swift +for w in 0.. 0)**: We need to find out the answer to `[l, l + 2**w)` for some **l**. +This interval, like all of our intervals in our table has a width that +is a power of two (e.g. 2, 4, 8, 16) ... so we can cut it into two equal halves. + - Our interval with width ``2**w`` is cut into two intervals, each of width ``2**(w - 1)``. + - Because each half has a width that is a power of two, we can look them up in our Sparse Table. + - We combine them together using **f**. + ``` + table[w][l] = f(table[w - 1][l], table[w - 1][l + 2 ** (w - 1)]) + ``` + + +![Sparse Table](Images/recursion.png) + +For example for `a = [ 10, 6, 5, -7, 9, -8, 2, 4, 20 ]` and **f** = *min*: + +- we compute `table[0][2] = 5`. We just had to look at `a[2]` because the range has a width of one. +- we compute `table[1][7] = 4`. We looked at `table[0][7]` and `table[0][8]` and apply **f** to them. +- we compute `table[3][1] = -8`. We looked at `table[2][1]` and `table[2][5]` and apply **f** to them. + + +![Sparse Table](Images/recursion_examples.png) + + + +```swift +public init(array: [T], function: @escaping (T, T) -> T, defaultT: T) { + let N = array.count + let W = Int(ceil(log2(Double(N)))) + table = [[T]](repeating: [T](repeating: defaultT, count: N), count: W) + self.function = function + self.defaultT = defaultT + + for w in 0.. T { + let width = r - l + let W = Int(floor(log2(Double(width)))) + let lo = table[W][l] + let hi = table[W][r - (1 << W)] + return function(lo, hi) + } + ``` + +Finding answers to queries takes **O(1)** time. + +## Analysing Sparse Tables + +- **Query Time** - Both table lookups take constant time. All other operations inside `query` take constant time. +So answering a single query also takes constant time: **O(1)**. But instead of one query we're actually answering **Q** queries, +and we'll need time to built the table before-hand. +Overall time is: **O(NlgN + Q)** to build the table and answer all queries. +The naive approach is to do a for loop for each query. The overall time for the naive approach is: **O(NQ)**. +For very large **Q**, the naive approach will scale poorly. For example if `Q = O(N*N)` +then the naive approach is `O(N*N*N)` where a Sparse Table takes time `O(N*N)`. +- **Space**- The number of possible **w** is **lgN** and the number of possible **l** our table is **N**. So the table +has uses **O(NlgN)** additional space. + +### Comparison with Segment Trees + +- **Pre-processing** - Segment Trees take **O(N)** time to build and use **O(N)** space. Sparse Tables take **O(NlgN)** time to build and use **O(NlgN)** space. +- **Queries** - Segment Tree queries are **O(lgN)** time for any **f** (idempotent or not idempotent). Sparse Table queries are **O(1)** time if **f** is idempotent and are not supported if **f** is not idempotent. [†] +- **Replacing Items** - Segment Trees allow us to efficiently update an element in **a** and update the segment tree in **O(lgN)** time. Sparse Tables do not allow this to be done efficiently. If we were to update an element in **a**, we'd have to rebuild the Sparse Table all over again in **O(NlgN)** time. + + +[†] *Although technically, it's possible to rewrite the `query` method +to add support for non-idempotent functions. But in doing so, we'd bump up the time up from O(1) to O(lgn), +completely defeating the original purpose of Sparse Tables - supporting lightening quick queries. +In such a case, we'd be better off using a Segment Tree (or a Fenwick Tree)* + +## Summary + +That's it! See the playground for more examples involving Sparse Tables. +You'll see examples for: min, max, gcd, boolean operators and logical operators. + +### See also + +- [Segment Trees (Swift Algorithm Club)](https://github.com/raywenderlich/swift-algorithm-club/tree/master/Segment%20Tree) +- [How to write O(lgn) time query function to support non-idempontent functions (GeeksForGeeks)](https://www.geeksforgeeks.org/range-sum-query-using-sparse-table/) +- [Fenwick Trees / Binary Indexed Trees (TopCoder)](https://www.topcoder.com/community/data-science/data-science-tutorials/binary-indexed-trees/) +- [Semilattice (Wikipedia)](https://en.wikipedia.org/wiki/Semilattice) + +*Written for Swift Algorithm Club by [James Lawson](https://github.com/jameslawson)* + diff --git a/Sparse Table/Sparse Table.playground/Contents.swift b/Sparse Table/Sparse Table.playground/Contents.swift new file mode 100644 index 000000000..ef83263b0 --- /dev/null +++ b/Sparse Table/Sparse Table.playground/Contents.swift @@ -0,0 +1,144 @@ +// +// Swift Algorithm Club - Sparse Table +// Author: James Lawson (github.com/jameslawson) +// + +import Foundation + +// Last checked with Xcode Version 9.2 (9C40b) +#if swift(>=4.0) +print("Hello, Swift 4!") +#endif + +public class SparseTable { + private var defaultT: T + private var table: [[T]] + private var function: (T, T) -> T + + public init(array: [T], function: @escaping (T, T) -> T, defaultT: T) { + let N = array.count + let W = Int(ceil(log2(Double(N)))) + table = [[T]](repeating: [T](repeating: defaultT, count: N), count: W) + self.function = function + self.defaultT = defaultT + + for w in 0.. T { + let width = r - l + let N = table[0].count + if width <= 0 || l >= N { + return defaultT + } + let r = min(N, r) + let W = Int(floor(log2(Double(width)))) + let lo = table[W][l] + let hi = table[W][r - (1 << W)] + return function(lo, hi) + } +} + +print("---------------------------- EXAMPLE 1 -------------------------------------") +// Here we have an array of integers and we're repeatedly +// finding the minimum over various ranges + +let intArray = [1, -11, -7, 3, 2, 4] +let minIntTable = SparseTable(array: intArray, function: min, defaultT: Int.max) +print(minIntTable.query(from: 0, until: 6)) // min(1, 3, -11, 3, 2, 4) = -11 +print(minIntTable.query(from: 3, until: 5)) // min(3, 2) = 2 +print(minIntTable.query(from: 2, until: 6)) // min(-7, 3, 2, 4) = -7 +print(minIntTable.query(from: 0, until: 1)) // min(1) = 1 +print(minIntTable.query(from: 0, until: 0)) // min() = Int.max +print("----------------------------------------------------------------------------\n\n") + + +print("---------------------------- EXAMPLE 2 -------------------------------------") +// Now we have an array of doubles and we're repeatedly +// finding the maximum over various ranges + +let doubleArray = [1.5, 20.0, 3.5, 15.0, 18.0, -10.0, 5.5] +let maxDoubleTable = SparseTable(array: doubleArray, function: max, defaultT: -.infinity) +print(maxDoubleTable.query(from: 0, until: 4)) // max(1.5, 20.0, 3.5, 15.0) = 20.0 +print(maxDoubleTable.query(from: 3, until: 4)) // max(3.5, 15.0) = 15.0 +print(maxDoubleTable.query(from: 4, until: 6)) // max(18.0, -10.0, 5.5) = 18.0 +print(maxDoubleTable.query(from: 1, until: 2)) // max(20.0) = 20.0 +print(maxDoubleTable.query(from: 0, until: 0)) // max() = -inf +print("----------------------------------------------------------------------------\n\n") + + +print("---------------------------- EXAMPLE 3 -------------------------------------") +// An array of booleans and we're repeatedly +// finding the boolean AND over various ranges + +let boolArray = [true, false, true, true, true, false, false] +func and(_ x: Bool, _ y: Bool) -> Bool { return x && y } + +let maxBoolTable = SparseTable(array: boolArray, function: and, defaultT: false) +print(maxBoolTable.query(from: 0, until: 4)) // and(T, F, T, T) = F +print(maxBoolTable.query(from: 2, until: 5)) // and(T, T, T) = T +print(maxBoolTable.query(from: 2, until: 6)) // and(T, T, T, F) = F +print(maxBoolTable.query(from: 0, until: 1)) // and(T) = T +print(maxBoolTable.query(from: 1, until: 2)) // and(F) = F +print(maxBoolTable.query(from: 0, until: 0)) // and() = F +print("----------------------------------------------------------------------------\n\n") + +print("---------------------------- EXAMPLE 4 -------------------------------------") +// An array of positive integers and we're repeatedly finding +// the gcd (greatest common divisor) over various ranges. The gcd operator is +// associative and idempotent so we can use it with sparse tables + +let posIntArray = [7, 2, 3, 4, 6, 5, 25, 75, 100] +func gcd(_ m: Int, _ n: Int) -> Int { + var a = 0 + var b = max(m, n) + var r = min(m, n) + + while r != 0 { + a = b + b = r + r = a % b + } + return b +} + +let gcdTable = SparseTable(array: posIntArray, function: gcd, defaultT: 1) +print(gcdTable.query(from: 0, until: 4)) // gcd(7, 2, 3) = 1 +print(gcdTable.query(from: 3, until: 5)) // gcd(4, 6) = 2 +print(gcdTable.query(from: 5, until: 7)) // gcd(5, 25, 75) = 5 +print(gcdTable.query(from: 6, until: 9)) // gcd(25, 75, 100) = 25 +print(gcdTable.query(from: 3, until: 4)) // gcd(4) = 4 +print(gcdTable.query(from: 0, until: 0)) // gcd() = 1 +print("------------------------------------------------------------------------\n\n") + + + +print("---------------------------- EXAMPLE 5 -------------------------------------") +// An array of nonnegative integers where for each integer we consider its binary representation. +// We're repeatedly finding the binary OR (|) over various ranges. The binary operator is +// associative and idempotent so we can use it with sparse tables + +let binArray = [0b1001, 0b1100, 0b0000, 0b0001, 0b0010, 0b0100, 0b0000, 0b1111] + + +let orTable = SparseTable(array: binArray, function: |, defaultT: 0b0000) +print(String(orTable.query(from: 0, until: 2), radix: 2)) // binary_or(1001, 1100) = 1101 +print(String(orTable.query(from: 3, until: 5), radix: 2)) // binary_or(0001, 0010) = 0011 + +print(String(orTable.query(from: 3, until: 6), radix: 2)) // binary_or(0001, 0010, 0100) = 0111 +print(String(orTable.query(from: 6, until: 8), radix: 2)) // binary_or(0000, 1111) = 1111 +print(String(orTable.query(from: 1, until: 5), radix: 2)) // binary_or(1100, 0000, 0001, 0010) = 1111 +print(String(orTable.query(from: 0, until: 1), radix: 2)) // binary_or(1001) = 1001 +print(String(orTable.query(from: 0, until: 0), radix: 2)) // binary_or() = 0000 +print("----------------------------------------------------------------------------\n\n") diff --git a/Sparse Table/Sparse Table.playground/contents.xcplayground b/Sparse Table/Sparse Table.playground/contents.xcplayground new file mode 100644 index 000000000..5da2641c9 --- /dev/null +++ b/Sparse Table/Sparse Table.playground/contents.xcplayground @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Sparse Table/Sparse Table.playground/playground.xcworkspace/contents.xcworkspacedata b/Sparse Table/Sparse Table.playground/playground.xcworkspace/contents.xcworkspacedata new file mode 100644 index 000000000..919434a62 --- /dev/null +++ b/Sparse Table/Sparse Table.playground/playground.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Splay Tree/readme.md b/Splay Tree/readme.md index 2c5e87ed7..75c3c0389 100644 --- a/Splay Tree/readme.md +++ b/Splay Tree/readme.md @@ -159,7 +159,7 @@ Now suppose a *insert(7)* operation was performed and we're in a *ZigZag* case. ## Advantages -Splay trees provide an efficient way to quickly access elements that are frequently requested. This characteristic makes then a good choice to implement, for exmaple, caches or garbage collection algorithms, or in any other problem involving frequent access to a certain numbers of elements from a data set. +Splay trees provide an efficient way to quickly access elements that are frequently requested. This characteristic makes then a good choice to implement, for example, caches or garbage collection algorithms, or in any other problem involving frequent access to a certain numbers of elements from a data set. ## Disadvantages @@ -263,7 +263,7 @@ If the rotations would had been taking first the parent and not the grandparent [Splay Tree on Wikipedia](https://en.wikipedia.org/wiki/Splay_tree) -[Splay Tree by University of California in Berkeley - CS 61B Lecture 34](https://www.youtube.com/watch?v=G5QIXywcJlY) +[Splay Tree by University of California in Berkeley - CS 61B Lecture 34](https://www.youtube.com/watch?v=8Zs1lj_bUV0) ---------------- diff --git a/Stack/Stack.playground/Contents.swift b/Stack/Stack.playground/Contents.swift index e9e3e9a57..e0632f8b4 100644 --- a/Stack/Stack.playground/Contents.swift +++ b/Stack/Stack.playground/Contents.swift @@ -1,10 +1,4 @@ - -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - -/* +/** Stack A stack is like an array but with limited functionality. You can only push @@ -15,27 +9,51 @@ print("Hello, Swift 4!") last is the first one to come off with the next pop. Push and pop are O(1) operations. + + ## Usage + ``` + var myStack = Stack(array: []) + myStack.push(10) + myStack.push(3) + myStack.push(57) + myStack.pop() // 57 + myStack.pop() // 3 + ``` */ - public struct Stack { + + /// Datastructure consisting of a generic item. fileprivate var array = [T]() + /// The number of items in the stack. public var count: Int { return array.count } + /// Verifies if the stack is empty. public var isEmpty: Bool { return array.isEmpty } + /** + Pushes an item to the top of the stack. + + - Parameter element: The item being pushed. + */ public mutating func push(_ element: T) { array.append(element) } + /** + Removes and returns the item at the top of the stack. + + - Returns: The item at the top of the stack. + */ public mutating func pop() -> T? { return array.popLast() } + /// Returns the item at the top of the stack. public var top: T? { return array.last } diff --git a/Stack/Stack.playground/contents.xcplayground b/Stack/Stack.playground/contents.xcplayground index 5da2641c9..a40c0f554 100644 --- a/Stack/Stack.playground/contents.xcplayground +++ b/Stack/Stack.playground/contents.xcplayground @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/Strassen Matrix Multiplication/StrassensMatrixMultiplication.playground/Contents.swift b/Strassen Matrix Multiplication/StrassensMatrixMultiplication.playground/Contents.swift index 409e28c52..6b9314557 100644 --- a/Strassen Matrix Multiplication/StrassensMatrixMultiplication.playground/Contents.swift +++ b/Strassen Matrix Multiplication/StrassensMatrixMultiplication.playground/Contents.swift @@ -1,10 +1,5 @@ //: Playground - noun: a place where people can play -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - import Foundation var A = Matrix(rows: 2, columns: 4, initialValue: 0) diff --git a/Threaded Binary Tree/ThreadedBinaryTree.playground/Contents.swift b/Threaded Binary Tree/ThreadedBinaryTree.playground/Contents.swift index 4ce9d8157..ece1966e0 100644 --- a/Threaded Binary Tree/ThreadedBinaryTree.playground/Contents.swift +++ b/Threaded Binary Tree/ThreadedBinaryTree.playground/Contents.swift @@ -1,10 +1,5 @@ //: Playground - noun: a place where people can play -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - // Simple little debug function to make testing output pretty func check(_ tree: ThreadedBinaryTree?) { if let tree = tree { diff --git a/Threaded Binary Tree/ThreadedBinaryTree.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Threaded Binary Tree/ThreadedBinaryTree.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Threaded Binary Tree/ThreadedBinaryTree.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Topological Sort/TopologicalSort1.swift b/Topological Sort/TopologicalSort1.swift index 2ad9b51a0..05f1ab887 100644 --- a/Topological Sort/TopologicalSort1.swift +++ b/Topological Sort/TopologicalSort1.swift @@ -1,6 +1,7 @@ extension Graph { private func depthFirstSearch(_ source: Node, visited: inout [Node : Bool]) -> [Node] { var result = [Node]() + visited[source] = true if let adjacencyList = adjacencyList(forNode: source) { for nodeInAdjacencyList in adjacencyList { @@ -10,7 +11,6 @@ extension Graph { } } - visited[source] = true return [source] + result } diff --git a/Tree/Tree.playground/Contents.swift b/Tree/Tree.playground/Contents.swift index 49cf41054..1fad157a0 100644 --- a/Tree/Tree.playground/Contents.swift +++ b/Tree/Tree.playground/Contents.swift @@ -1,10 +1,5 @@ //: Playground - noun: a place where people can play -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - let tree = TreeNode(value: "beverages") let hotNode = TreeNode(value: "hot") diff --git a/Tree/Tree.playground/contents.xcplayground b/Tree/Tree.playground/contents.xcplayground index 06828af92..69d154d1e 100644 --- a/Tree/Tree.playground/contents.xcplayground +++ b/Tree/Tree.playground/contents.xcplayground @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/Trie/ReadMe.md b/Trie/ReadMe.md index 34b9beb9c..8d0c45c8a 100644 --- a/Trie/ReadMe.md +++ b/Trie/ReadMe.md @@ -27,29 +27,29 @@ Tries are very useful for certain situations. Here are some of the advantages: ```swift func contains(word: String) -> Bool { - guard !word.isEmpty else { return false } + guard !word.isEmpty else { return false } - // 1 - var currentNode = root + // 1 + var currentNode = root - // 2 - var characters = Array(word.lowercased().characters) - var currentIndex = 0 + // 2 + var characters = Array(word.lowercased()) + var currentIndex = 0 - // 3 - while currentIndex < characters.count, - let child = currentNode.children[characters[currentIndex]] { - - currentNode = child - currentIndex += 1 - } - - // 4 - if currentIndex == characters.count && currentNode.isTerminating { - return true - } else { - return false - } + // 3 + while currentIndex < characters.count, + let child = currentNode.children[characters[currentIndex]] { + + currentNode = child + currentIndex += 1 + } + + // 4 + if currentIndex == characters.count && currentNode.isTerminating { + return true + } else { + return false + } } ``` @@ -74,7 +74,7 @@ func insert(word: String) { var currentNode = root // 2 - for character in word.lowercased().characters { + for character in word.lowercased() { // 3 if let childNode = currentNode.children[character] { currentNode = childNode diff --git a/Trie/Trie/Trie.xcodeproj/project.pbxproj b/Trie/Trie/Trie.xcodeproj/project.pbxproj index 90c5734cc..6b7cc9b61 100644 --- a/Trie/Trie/Trie.xcodeproj/project.pbxproj +++ b/Trie/Trie/Trie.xcodeproj/project.pbxproj @@ -87,7 +87,9 @@ EB798E191DFEF79900F0628D /* TrieUITests */, EB798DFB1DFEF79900F0628D /* Products */, ); + indentWidth = 2; sourceTree = ""; + tabWidth = 2; }; EB798DFB1DFEF79900F0628D /* Products */ = { isa = PBXGroup; @@ -195,20 +197,23 @@ isa = PBXProject; attributes = { LastSwiftUpdateCheck = 0810; - LastUpgradeCheck = 0820; + LastUpgradeCheck = 1000; ORGANIZATIONNAME = "Rick Zaccone"; TargetAttributes = { EB798DF91DFEF79900F0628D = { CreatedOnToolsVersion = 8.1; + LastSwiftMigration = 1000; ProvisioningStyle = Automatic; }; EB798E0A1DFEF79900F0628D = { CreatedOnToolsVersion = 8.1; + LastSwiftMigration = 1000; ProvisioningStyle = Automatic; TestTargetID = EB798DF91DFEF79900F0628D; }; EB798E151DFEF79900F0628D = { CreatedOnToolsVersion = 8.1; + LastSwiftMigration = 1000; ProvisioningStyle = Automatic; TestTargetID = EB798DF91DFEF79900F0628D; }; @@ -326,15 +331,23 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = 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_SUSPICIOUS_MOVES = YES; CLANG_WARN_UNREACHABLE_CODE = YES; @@ -376,15 +389,23 @@ CLANG_CXX_LIBRARY = "libc++"; CLANG_ENABLE_MODULES = YES; CLANG_ENABLE_OBJC_ARC = 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_SUSPICIOUS_MOVES = YES; CLANG_WARN_UNREACHABLE_CODE = YES; @@ -418,7 +439,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = edu.bucknell.zaccone.Trie; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.2; }; name = Debug; }; @@ -431,7 +452,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = edu.bucknell.zaccone.Trie; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.2; }; name = Release; }; @@ -445,7 +466,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = edu.bucknell.zaccone.TrieTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.2; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Trie.app/Contents/MacOS/Trie"; }; name = Debug; @@ -460,7 +481,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = edu.bucknell.zaccone.TrieTests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.2; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Trie.app/Contents/MacOS/Trie"; }; name = Release; @@ -474,7 +495,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = edu.bucknell.zaccone.TrieUITests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.2; TEST_TARGET_NAME = Trie; }; name = Debug; @@ -488,7 +509,7 @@ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; PRODUCT_BUNDLE_IDENTIFIER = edu.bucknell.zaccone.TrieUITests; PRODUCT_NAME = "$(TARGET_NAME)"; - SWIFT_VERSION = 3.0; + SWIFT_VERSION = 4.2; TEST_TARGET_NAME = Trie; }; name = Release; diff --git a/Trie/Trie/Trie.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Trie/Trie/Trie.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Trie/Trie/Trie.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Trie/Trie/Trie/AppDelegate.swift b/Trie/Trie/Trie/AppDelegate.swift index b2c46695b..48dc24673 100644 --- a/Trie/Trie/Trie/AppDelegate.swift +++ b/Trie/Trie/Trie/AppDelegate.swift @@ -11,12 +11,12 @@ import Cocoa @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { - func applicationDidFinishLaunching(_ aNotification: Notification) { - // Insert code here to initialize your application - } + func applicationDidFinishLaunching(_ aNotification: Notification) { + // Insert code here to initialize your application + } - func applicationWillTerminate(_ aNotification: Notification) { - // Insert code here to tear down your application - } + func applicationWillTerminate(_ aNotification: Notification) { + // Insert code here to tear down your application + } } diff --git a/Trie/Trie/Trie/Trie.swift b/Trie/Trie/Trie/Trie.swift index 1a7017c4c..accc7b61c 100644 --- a/Trie/Trie/Trie/Trie.swift +++ b/Trie/Trie/Trie/Trie.swift @@ -6,11 +6,6 @@ // Copyright © 2016 Rick Zaccone. All rights reserved. // -// last checked with Xcode 9.0b4 -#if swift(>=4.0) - print("Hello, Swift 4!") -#endif - import Foundation /// A node in the trie @@ -48,54 +43,53 @@ class TrieNode { /// A trie data structure containing words. Each node is a single /// character of a word. class Trie: NSObject, NSCoding { - typealias Node = TrieNode - /// The number of words in the trie - public var count: Int { - return wordCount - } - /// Is the trie empty? - public var isEmpty: Bool { - return wordCount == 0 - } - /// All words currently in the trie - public var words: [String] { - return wordsInSubtrie(rootNode: root, partialWord: "") - } - fileprivate let root: Node - fileprivate var wordCount: Int - - /// Creates an empty trie. - override init() { - root = Node() - wordCount = 0 - super.init() - } + typealias Node = TrieNode + /// The number of words in the trie + public var count: Int { + return wordCount + } + /// Is the trie empty? + public var isEmpty: Bool { + return wordCount == 0 + } + /// All words currently in the trie + public var words: [String] { + return wordsInSubtrie(rootNode: root, partialWord: "") + } + fileprivate let root: Node + fileprivate var wordCount: Int + + /// Creates an empty trie. + override init() { + root = Node() + wordCount = 0 + super.init() + } - // MARK: NSCoding + // MARK: NSCoding - /// Initializes the trie with words from an archive - /// - /// - Parameter decoder: Decodes the archive - required convenience init?(coder decoder: NSCoder) { - self.init() - let words = decoder.decodeObject(forKey: "words") as? [String] - for word in words! { - self.insert(word: word) - } + /// Initializes the trie with words from an archive + /// + /// - Parameter decoder: Decodes the archive + required convenience init?(coder decoder: NSCoder) { + self.init() + let words = decoder.decodeObject(forKey: "words") as? [String] + for word in words! { + self.insert(word: word) } + } - /// Encodes the words in the trie by putting them in an array then encoding - /// the array. - /// - /// - Parameter coder: The object that will encode the array - func encode(with coder: NSCoder) { - coder.encode(self.words, forKey: "words") - } + /// Encodes the words in the trie by putting them in an array then encoding + /// the array. + /// + /// - Parameter coder: The object that will encode the array + func encode(with coder: NSCoder) { + coder.encode(self.words, forKey: "words") + } } // MARK: - Adds methods: insert, remove, contains extension Trie { - /// Inserts a word into the trie. If the word is already present, /// there is no change. /// @@ -105,7 +99,7 @@ extension Trie { return } var currentNode = root - for character in word.lowercased().characters { + for character in word.lowercased() { if let childNode = currentNode.children[character] { currentNode = childNode } else { @@ -130,7 +124,7 @@ extension Trie { return false } var currentNode = root - for character in word.lowercased().characters { + for character in word.lowercased() { guard let childNode = currentNode.children[character] else { return false } @@ -147,14 +141,14 @@ extension Trie { /// - Returns: the node where the search ended, nil if the /// search failed. private func findLastNodeOf(word: String) -> Node? { - var currentNode = root - for character in word.lowercased().characters { - guard let childNode = currentNode.children[character] else { - return nil - } - currentNode = childNode + var currentNode = root + for character in word.lowercased() { + guard let childNode = currentNode.children[character] else { + return nil } - return currentNode + currentNode = childNode + } + return currentNode } /// Attempts to walk to the terminating node of a word. The @@ -165,10 +159,9 @@ extension Trie { /// search failed. private func findTerminalNodeOf(word: String) -> Node? { if let lastNode = findLastNodeOf(word: word) { - return lastNode.isTerminating ? lastNode : nil + return lastNode.isTerminating ? lastNode : nil } return nil - } /// Deletes a word from the trie by starting with the last letter @@ -242,17 +235,17 @@ extension Trie { /// - prefix: the letters for word prefix /// - Returns: the words in the subtrie that start with prefix func findWordsWithPrefix(prefix: String) -> [String] { - var words = [String]() - let prefixLowerCased = prefix.lowercased() - if let lastNode = findLastNodeOf(word: prefixLowerCased) { - if lastNode.isTerminating { - words.append(prefixLowerCased) - } - for childNode in lastNode.children.values { - let childWords = wordsInSubtrie(rootNode: childNode, partialWord: prefixLowerCased) - words += childWords - } + var words = [String]() + let prefixLowerCased = prefix.lowercased() + if let lastNode = findLastNodeOf(word: prefixLowerCased) { + if lastNode.isTerminating { + words.append(prefixLowerCased) + } + for childNode in lastNode.children.values { + let childWords = wordsInSubtrie(rootNode: childNode, partialWord: prefixLowerCased) + words += childWords } - return words + } + return words } } diff --git a/Trie/Trie/Trie/ViewController.swift b/Trie/Trie/Trie/ViewController.swift index 9c5e66163..8ce34f6c7 100644 --- a/Trie/Trie/Trie/ViewController.swift +++ b/Trie/Trie/Trie/ViewController.swift @@ -6,25 +6,20 @@ // Copyright © 2016 Rick Zaccone. All rights reserved. // -// last checked with Xcode 9.0b4 -#if swift(>=4.0) - print("Hello, Swift 4!") -#endif - import Cocoa class ViewController: NSViewController { - override func viewDidLoad() { - super.viewDidLoad() + override func viewDidLoad() { + super.viewDidLoad() - // Do any additional setup after loading the view. - } + // Do any additional setup after loading the view. + } - override var representedObject: Any? { - didSet { - // Update the view, if already loaded. - } + override var representedObject: Any? { + didSet { + // Update the view, if already loaded. } + } } diff --git a/Trie/Trie/TrieTests/TrieTests.swift b/Trie/Trie/TrieTests/TrieTests.swift index 45be03aad..0c5bc69c6 100644 --- a/Trie/Trie/TrieTests/TrieTests.swift +++ b/Trie/Trie/TrieTests/TrieTests.swift @@ -6,195 +6,188 @@ // Copyright © 2016 Rick Zaccone. All rights reserved. // -// last checked with Xcode 9.0b4 -#if swift(>=4.0) - print("Hello, Swift 4!") -#endif - import XCTest @testable import Trie class TrieTests: XCTestCase { - var wordArray: [String]? - var trie = Trie() - - /// Makes sure that the wordArray and trie are initialized before each test. - override func setUp() { - super.setUp() - createWordArray() - insertWordsIntoTrie() + var wordArray: [String]? + var trie = Trie() + + /// Makes sure that the wordArray and trie are initialized before each test. + override func setUp() { + super.setUp() + createWordArray() + insertWordsIntoTrie() + } + + /// Don't need to do anything here because the wordArray and trie should + /// stay around. + override func tearDown() { + super.tearDown() + } + + /// Reads words from the dictionary file and inserts them into an array. If + /// the word array already has words, do nothing. This allows running all + /// tests without repeatedly filling the array with the same values. + func createWordArray() { + guard wordArray == nil else { + return } - - /// Don't need to do anything here because the wordArray and trie should - /// stay around. - override func tearDown() { - super.tearDown() + let resourcePath = Bundle.main.resourcePath! as NSString + let fileName = "dictionary.txt" + let filePath = resourcePath.appendingPathComponent(fileName) + + var data: String? + do { + data = try String(contentsOfFile: filePath, encoding: String.Encoding.utf8) + } catch let error as NSError { + XCTAssertNil(error) } - - /// Reads words from the dictionary file and inserts them into an array. If - /// the word array already has words, do nothing. This allows running all - /// tests without repeatedly filling the array with the same values. - func createWordArray() { - guard wordArray == nil else { - return - } - let resourcePath = Bundle.main.resourcePath! as NSString - let fileName = "dictionary.txt" - let filePath = resourcePath.appendingPathComponent(fileName) - - var data: String? - do { - data = try String(contentsOfFile: filePath, encoding: String.Encoding.utf8) - } catch let error as NSError { - XCTAssertNil(error) - } - XCTAssertNotNil(data) - let dictionarySize = 162825 - wordArray = data!.components(separatedBy: "\n") - XCTAssertEqual(wordArray!.count, dictionarySize) + XCTAssertNotNil(data) + let dictionarySize = 162825 + wordArray = data!.components(separatedBy: "\n") + XCTAssertEqual(wordArray!.count, dictionarySize) + } + + /// Inserts words into a trie. If the trie is non-empty, don't do anything. + func insertWordsIntoTrie() { + guard let wordArray = wordArray, trie.count == 0 else { return } + for word in wordArray { + trie.insert(word: word) } - - /// Inserts words into a trie. If the trie is non-empty, don't do anything. - func insertWordsIntoTrie() { - guard let wordArray = wordArray, trie.count == 0 else { return } - for word in wordArray { - trie.insert(word: word) - } + } + + /// Tests that a newly created trie has zero words. + func testCreate() { + let trie = Trie() + XCTAssertEqual(trie.count, 0) + } + + /// Tests the insert method + func testInsert() { + let trie = Trie() + trie.insert(word: "cute") + trie.insert(word: "cutie") + trie.insert(word: "fred") + XCTAssertTrue(trie.contains(word: "cute")) + XCTAssertFalse(trie.contains(word: "cut")) + trie.insert(word: "cut") + XCTAssertTrue(trie.contains(word: "cut")) + XCTAssertEqual(trie.count, 4) + } + + /// Tests the remove method + func testRemove() { + let trie = Trie() + trie.insert(word: "cute") + trie.insert(word: "cut") + XCTAssertEqual(trie.count, 2) + trie.remove(word: "cute") + XCTAssertTrue(trie.contains(word: "cut")) + XCTAssertFalse(trie.contains(word: "cute")) + XCTAssertEqual(trie.count, 1) + } + + /// Tests the words property + func testWords() { + let trie = Trie() + var words = trie.words + XCTAssertEqual(words.count, 0) + trie.insert(word: "foobar") + words = trie.words + XCTAssertEqual(words[0], "foobar") + XCTAssertEqual(words.count, 1) + } + + /// Tests the performance of the insert method. + func testInsertPerformance() { + self.measure { + let trie = Trie() + for word in self.wordArray! { + trie.insert(word: word) + } } - - /// Tests that a newly created trie has zero words. - func testCreate() { - let trie = Trie() - XCTAssertEqual(trie.count, 0) + XCTAssertGreaterThan(trie.count, 0) + XCTAssertEqual(trie.count, wordArray?.count) + } + + /// Tests the performance of the insert method when the words are already + /// present. + func testInsertAgainPerformance() { + self.measure { + for word in self.wordArray! { + self.trie.insert(word: word) + } } - - /// Tests the insert method - func testInsert() { - let trie = Trie() - trie.insert(word: "cute") - trie.insert(word: "cutie") - trie.insert(word: "fred") - XCTAssertTrue(trie.contains(word: "cute")) - XCTAssertFalse(trie.contains(word: "cut")) - trie.insert(word: "cut") - XCTAssertTrue(trie.contains(word: "cut")) - XCTAssertEqual(trie.count, 4) + } + + /// Tests the performance of the contains method. + func testContainsPerformance() { + self.measure { + for word in self.wordArray! { + XCTAssertTrue(self.trie.contains(word: word)) + } } + } - /// Tests the remove method - func testRemove() { - let trie = Trie() - trie.insert(word: "cute") - trie.insert(word: "cut") - XCTAssertEqual(trie.count, 2) - trie.remove(word: "cute") - XCTAssertTrue(trie.contains(word: "cut")) - XCTAssertFalse(trie.contains(word: "cute")) - XCTAssertEqual(trie.count, 1) + /// Tests the performance of the remove method. Since setup has already put + /// words into the trie, remove them before measuring performance. + func testRemovePerformance() { + for word in self.wordArray! { + self.trie.remove(word: word) } - - /// Tests the words property - func testWords() { - let trie = Trie() - var words = trie.words - XCTAssertEqual(words.count, 0) - trie.insert(word: "foobar") - words = trie.words - XCTAssertEqual(words[0], "foobar") - XCTAssertEqual(words.count, 1) + self.measure { + self.insertWordsIntoTrie() + for word in self.wordArray! { + self.trie.remove(word: word) + } } - - /// Tests the performance of the insert method. - func testInsertPerformance() { - self.measure { - let trie = Trie() - for word in self.wordArray! { - trie.insert(word: word) - } - } - XCTAssertGreaterThan(trie.count, 0) - XCTAssertEqual(trie.count, wordArray?.count) + XCTAssertEqual(trie.count, 0) + } + + /// Tests the performance of the words computed property. Also tests to see + /// if it worked properly. + func testWordsPerformance() { + var words: [String]? + self.measure { + words = self.trie.words } - - /// Tests the performance of the insert method when the words are already - /// present. - func testInsertAgainPerformance() { - self.measure { - for word in self.wordArray! { - self.trie.insert(word: word) - } - } - } - - /// Tests the performance of the contains method. - func testContainsPerformance() { - self.measure { - for word in self.wordArray! { - XCTAssertTrue(self.trie.contains(word: word)) - } - } - } - - /// Tests the performance of the remove method. Since setup has already put - /// words into the trie, remove them before measuring performance. - func testRemovePerformance() { - for word in self.wordArray! { - self.trie.remove(word: word) - } - self.measure { - self.insertWordsIntoTrie() - for word in self.wordArray! { - self.trie.remove(word: word) - } - } - XCTAssertEqual(trie.count, 0) - } - - /// Tests the performance of the words computed property. Also tests to see - /// if it worked properly. - func testWordsPerformance() { - var words: [String]? - self.measure { - words = self.trie.words - } - XCTAssertEqual(words?.count, trie.count) - for word in words! { - XCTAssertTrue(self.trie.contains(word: word)) - } - } - - /// Tests the archiving and unarchiving of the trie. - func testArchiveAndUnarchive() { - let resourcePath = Bundle.main.resourcePath! as NSString - let fileName = "dictionary-archive" - let filePath = resourcePath.appendingPathComponent(fileName) - NSKeyedArchiver.archiveRootObject(trie, toFile: filePath) - let trieCopy = NSKeyedUnarchiver.unarchiveObject(withFile: filePath) as? Trie - XCTAssertEqual(trieCopy?.count, trie.count) - - } - - func testFindWordsWithPrefix() { - let trie = Trie() - trie.insert(word: "test") - trie.insert(word: "another") - trie.insert(word: "exam") - let wordsAll = trie.findWordsWithPrefix(prefix: "") - XCTAssertEqual(wordsAll.sorted(), ["another", "exam", "test"]) - let words = trie.findWordsWithPrefix(prefix: "ex") - XCTAssertEqual(words, ["exam"]) - trie.insert(word: "examination") - let words2 = trie.findWordsWithPrefix(prefix: "exam") - XCTAssertEqual(words2, ["exam", "examination"]) - let noWords = trie.findWordsWithPrefix(prefix: "tee") - XCTAssertEqual(noWords, []) - let unicodeWord = "😬😎" - trie.insert(word: unicodeWord) - let wordsUnicode = trie.findWordsWithPrefix(prefix: "😬") - XCTAssertEqual(wordsUnicode, [unicodeWord]) - trie.insert(word: "Team") - let wordsUpperCase = trie.findWordsWithPrefix(prefix: "Te") - XCTAssertEqual(wordsUpperCase.sorted(), ["team", "test"]) - + XCTAssertEqual(words?.count, trie.count) + for word in words! { + XCTAssertTrue(self.trie.contains(word: word)) } + } + + /// Tests the archiving and unarchiving of the trie. + func testArchiveAndUnarchive() { + let resourcePath = Bundle.main.resourcePath! as NSString + let fileName = "dictionary-archive" + let filePath = resourcePath.appendingPathComponent(fileName) + NSKeyedArchiver.archiveRootObject(trie, toFile: filePath) + let trieCopy = NSKeyedUnarchiver.unarchiveObject(withFile: filePath) as? Trie + XCTAssertEqual(trieCopy?.count, trie.count) + } + + func testFindWordsWithPrefix() { + let trie = Trie() + trie.insert(word: "test") + trie.insert(word: "another") + trie.insert(word: "exam") + let wordsAll = trie.findWordsWithPrefix(prefix: "") + XCTAssertEqual(wordsAll.sorted(), ["another", "exam", "test"]) + let words = trie.findWordsWithPrefix(prefix: "ex") + XCTAssertEqual(words, ["exam"]) + trie.insert(word: "examination") + let words2 = trie.findWordsWithPrefix(prefix: "exam") + XCTAssertEqual(words2, ["exam", "examination"]) + let noWords = trie.findWordsWithPrefix(prefix: "tee") + XCTAssertEqual(noWords, []) + let unicodeWord = "😬😎" + trie.insert(word: unicodeWord) + let wordsUnicode = trie.findWordsWithPrefix(prefix: "😬") + XCTAssertEqual(wordsUnicode, [unicodeWord]) + trie.insert(word: "Team") + let wordsUpperCase = trie.findWordsWithPrefix(prefix: "Te") + XCTAssertEqual(wordsUpperCase.sorted(), ["team", "test"]) + } } diff --git a/Trie/Trie/TrieUITests/TrieUITests.swift b/Trie/Trie/TrieUITests/TrieUITests.swift index acb3b1756..380bf3092 100644 --- a/Trie/Trie/TrieUITests/TrieUITests.swift +++ b/Trie/Trie/TrieUITests/TrieUITests.swift @@ -10,27 +10,27 @@ import XCTest class TrieUITests: XCTestCase { - override func setUp() { - super.setUp() + override func setUp() { + super.setUp() - // Put setup code here. This method is called before the invocation of each test method in the class. + // Put setup code here. This method is called before the invocation of each test method in the class. - // In UI tests it is usually best to stop immediately when a failure occurs. - continueAfterFailure = false - // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. - XCUIApplication().launch() + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. + XCUIApplication().launch() - // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. - } + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } - override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. - super.tearDown() - } + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } - func testExample() { - // Use recording to get started writing UI tests. - // Use XCTAssert and related functions to verify your tests produce the correct results. - } + func testExample() { + // Use recording to get started writing UI tests. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } } diff --git a/Two-Sum Problem/README.markdown b/Two-Sum Problem/README.markdown index 4d8ffcd95..bbd033343 100644 --- a/Two-Sum Problem/README.markdown +++ b/Two-Sum Problem/README.markdown @@ -168,4 +168,4 @@ I'm quite enamored by this little algorithm. It shows that with some basic prepr * [3Sum / 4Sum](https://github.com/raywenderlich/swift-algorithm-club/tree/master/3Sum%20and%204Sum) -*Written for Swift Algorithm Club by Matthijs Hollemans and Daniel Speiser* +*Written for Swift Algorithm Club by Matthijs Hollemans and Daniel Speiser updated to swift 4.2 by Farrukh Askari* diff --git a/Two-Sum Problem/Solution 1/2Sum.playground/Contents.swift b/Two-Sum Problem/Solution 1/2Sum.playground/Contents.swift index 26bfae449..00a39b04a 100644 --- a/Two-Sum Problem/Solution 1/2Sum.playground/Contents.swift +++ b/Two-Sum Problem/Solution 1/2Sum.playground/Contents.swift @@ -1,8 +1,5 @@ //: Two Sum -// Last checked with: Version 9.0 (9A235) -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif +// Last checked with: Version 10.0 (10A255) func twoSum(_ nums: [Int], target: Int) -> (Int, Int)? { var dict = [Int: Int]() diff --git a/Two-Sum Problem/Solution 1/2Sum.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Two-Sum Problem/Solution 1/2Sum.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Two-Sum Problem/Solution 1/2Sum.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Two-Sum Problem/Solution 2/2Sum.playground/Contents.swift b/Two-Sum Problem/Solution 2/2Sum.playground/Contents.swift index 4cb3d9283..469c15b5f 100644 --- a/Two-Sum Problem/Solution 2/2Sum.playground/Contents.swift +++ b/Two-Sum Problem/Solution 2/2Sum.playground/Contents.swift @@ -1,8 +1,5 @@ //: Playground - noun: a place where people can play -// Last checked with: Version 9.0 beta 4 (9M189t) -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif +// Last checked with: Version 10.0 (10A255) func twoSumProblem(_ a: [Int], k: Int) -> ((Int, Int))? { var i = 0 diff --git a/Two-Sum Problem/Solution 2/2Sum.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Two-Sum Problem/Solution 2/2Sum.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Two-Sum Problem/Solution 2/2Sum.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Union-Find/UnionFind.playground/Contents.swift b/Union-Find/UnionFind.playground/Contents.swift index 6ac3b4c4e..b1f7fb4e1 100644 --- a/Union-Find/UnionFind.playground/Contents.swift +++ b/Union-Find/UnionFind.playground/Contents.swift @@ -1,10 +1,5 @@ //: Playground - noun: a place where people can play -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - var dsu = UnionFindQuickUnion() for i in 1...10 { diff --git a/Union-Find/UnionFind.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Union-Find/UnionFind.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 000000000..18d981003 --- /dev/null +++ b/Union-Find/UnionFind.playground/playground.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Z-Algorithm/README.markdown b/Z-Algorithm/README.markdown index 8b84c28bf..a6423be03 100644 --- a/Z-Algorithm/README.markdown +++ b/Z-Algorithm/README.markdown @@ -28,8 +28,7 @@ This a simple description of the idea that is behind this algorithm. There are a Here is the code of the function that computes the Z-array: ```swift func ZetaAlgorithm(ptrn: String) -> [Int]? { - - let pattern = Array(ptrn.characters) + let pattern = Array(ptrn) let patternLength: Int = pattern.count guard patternLength > 0 else { @@ -131,7 +130,7 @@ The Z-Algorithm discussed above leads to the simplest linear-time string matchin extension String { func indexesOf(pattern: String) -> [Int]? { - let patternLength: Int = pattern.characters.count + let patternLength: Int = pattern.count /* Let's calculate the Z-Algorithm on the concatenation of pattern and text */ let zeta = ZetaAlgorithm(ptrn: pattern + "💲" + self) diff --git a/Z-Algorithm/ZAlgorithm.swift b/Z-Algorithm/ZAlgorithm.swift index 969a3599b..65acaabd1 100644 --- a/Z-Algorithm/ZAlgorithm.swift +++ b/Z-Algorithm/ZAlgorithm.swift @@ -9,8 +9,7 @@ import Foundation func ZetaAlgorithm(ptrn: String) -> [Int]? { - - let pattern = Array(ptrn.characters) + let pattern = Array(ptrn) let patternLength = pattern.count guard patternLength > 0 else { diff --git a/Z-Algorithm/ZetaAlgorithm.playground/Contents.swift b/Z-Algorithm/ZetaAlgorithm.playground/Contents.swift index d057080a1..56aa92170 100644 --- a/Z-Algorithm/ZetaAlgorithm.playground/Contents.swift +++ b/Z-Algorithm/ZetaAlgorithm.playground/Contents.swift @@ -1,13 +1,7 @@ //: Playground - noun: a place where people can play -// last checked with Xcode 9.0b4 -#if swift(>=4.0) -print("Hello, Swift 4!") -#endif - func ZetaAlgorithm(ptrn: String) -> [Int]? { - - let pattern = Array(ptrn.characters) + let pattern = Array(ptrn) let patternLength = pattern.count guard patternLength > 0 else { @@ -65,7 +59,7 @@ func ZetaAlgorithm(ptrn: String) -> [Int]? { extension String { func indexesOf(pattern: String) -> [Int]? { - let patternLength = pattern.characters.count + let patternLength = pattern.count let zeta = ZetaAlgorithm(ptrn: pattern + "💲" + self) guard zeta != nil else { diff --git a/Z-Algorithm/ZetaAlgorithm.swift b/Z-Algorithm/ZetaAlgorithm.swift index e60c1617f..f2c453364 100644 --- a/Z-Algorithm/ZetaAlgorithm.swift +++ b/Z-Algorithm/ZetaAlgorithm.swift @@ -11,7 +11,7 @@ import Foundation extension String { func indexesOf(pattern: String) -> [Int]? { - let patternLength = pattern.characters.count + let patternLength = pattern.count let zeta = ZetaAlgorithm(ptrn: pattern + "💲" + self) guard zeta != nil else { diff --git a/gfm-render.sh b/gfm-render.sh new file mode 100755 index 000000000..4e18c8560 --- /dev/null +++ b/gfm-render.sh @@ -0,0 +1,141 @@ +#!/usr/bin/env bash + +set -e + +# $1 - readme file name +function render_markdown_to_html { + # escape escaping characters on Darwin only + content=$( + cat "$1" \ + | sed 's/\\/\\\\/g' \ + | sed 's/"/\\"/g' \ + | sed $'s/\t/\\\\t/g' \ + | sed -e ':a' -e 'N' -e '$!ba' -e 's/\n/\\\n/g' \ + ) + + # network call to GitHub API + json="{\"text\":\"$content\",\"mode\":\"gfm\",\"context\":\"$USERNAME/swift-algorithm-club\"}" + echo -e "$(curl -s --data "$json" -u $USERNAME:$TOKEN https://api.github.com/markdown)" +} + +# download github systax highlight stylesheet +echo "> Downloading github-light.css..." +curl -s -O https://raw.githubusercontent.com/primer/github-syntax-light/master/lib/github-light.css + +# slightly modify the main stylesheet +echo "> Modifying github-light.css..." +cat >> github-light.css << EOF +#container { + margin: 0 auto; + width: 75%; + min-width: 768px; + max-width: 896px; + position: relative; +} + +body { + font-size: 18px; +} + +code { + padding: 0.2em; + margin: 0; + font-size: 85%; + background-color: #f6f8fa; + line-height: 1.45; + border-radius: 3px +} + +pre code { + padding: 0px; + background-color: transparent; +} + +.highlight { + margin: 0px; + padding: 0px 16px; + font-size: 85%; + line-height: 1.45; + overflow: auto; + background-color: #f6f8fa; + border-radius: 3px; +} + +@media (max-width: 768px) { + #container { + position: absolute; + margin: 0; + width: 100%; + height: 100%; + min-width: 100%; + } +} +EOF + +# other markdown articles +for title in "What are Algorithms" "Big-O Notation" "Algorithm Design" "Why Algorithms"; do + echo "> Generating $title.html..." + + cat > "$title.html" << EOF + + + $title + + + +
$(render_markdown_to_html "$title.markdown")
+ + +EOF +done + +# if index.html does not exist, create one; +# otherwise, empty its content. +echo "> Generating index.html..." +cat > index.html << EOF + + + Swift Algorithm Club + + + +
$(render_markdown_to_html README.markdown | sed 's/.markdown/.html/g')
+ + +EOF + +# iterate immediate directories +find . -maxdepth 1 -type d | while read folder; do + readme='' + + # get the right extension for the README file if there is one + if [[ -f $folder/README.md ]]; then readme="$folder/README.md"; fi + if [[ -f $folder/README.markdown ]]; then readme="$folder/README.markdown"; fi + + # skip if there is no README or it it the README of the repository + if [[ (-z $readme) || $readme == "./README.markdown" ]]; then continue; fi + + # render README to HTML + name=$(basename "$folder") + echo "> Generating $name/index.html..." + + cat > "$folder/index.html" << EOF + + + $name + + + +
$(render_markdown_to_html "$readme")
+ + +EOF +done + +# push to gh-pages +if [[ $CI = true ]]; then + git checkout -b gh-pages + git add . + git commit -m "$Generated by TravisCI on $(date +%D)" + git push -f https://$TOKEN@github.com/$USERNAME/swift-algorithm-club.git gh-pages +fi diff --git a/swift-algorithm-club.xcworkspace/contents.xcworkspacedata b/swift-algorithm-club.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 4cab8e2d4..000000000 --- a/swift-algorithm-club.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,2870 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -