Skip to content

Commit

Permalink
Migrates Ternary Search Tree to Swift 3 syntax (kodecocodes#352)
Browse files Browse the repository at this point in the history
* migrates Ternary Search Tree to Swift 3 syntax

* aligning function names as suggested

* adds unit tests to TernarySearchTree

* adds guards as suggested
  • Loading branch information
pbodsk authored and vincentngo committed Jan 11, 2017
1 parent 6ec92d7 commit 53b8771
Show file tree
Hide file tree
Showing 12 changed files with 529 additions and 76 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,4 @@ script:
- 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
30 changes: 7 additions & 23 deletions Ternary Search Tree/TST.playground/Contents.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,39 +4,23 @@ import Cocoa
import Foundation

let treeOfStrings = TernarySearchTree<String>()
let allowedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"

//Random string generator from:
//http://stackoverflow.com/questions/26845307/generate-random-alphanumeric-string-in-swift/26845710
func randomAlphaNumericString(length: Int) -> String {
let allowedCharsCount = UInt32(allowedChars.characters.count)
var randomString = ""

for _ in (0..<length) {
let randomNum = Int(arc4random_uniform(allowedCharsCount))
let newCharacter = allowedChars[allowedChars.startIndex.advancedBy(randomNum)]
randomString += String(newCharacter)
}

return randomString
}

var testStrings: [(key: String, data: String)] = []
let testCount = 30
for _ in (1...testCount) {
let randomLength = Int(arc4random_uniform(10))
let key = randomAlphaNumericString(randomLength)
let data = randomAlphaNumericString(randomLength)
let key = Utils.shared.randomAlphaNumericString(withLength: randomLength)
let data = Utils.shared.randomAlphaNumericString(withLength: randomLength)
// print("Key: \(key) Data: \(data)")

if key != "" && data != "" {
testStrings.append((key, data))
treeOfStrings.insert(data, withKey: key)
treeOfStrings.insert(data: data, withKey: key)
}
}

for aTest in testStrings {
let data = treeOfStrings.find(aTest.key)
let data = treeOfStrings.find(key: aTest.key)

if data == nil {
print("TEST FAILED. Key: \(aTest.key) Data: \(aTest.data)")
Expand All @@ -51,16 +35,16 @@ let treeOfInts = TernarySearchTree<Int>()
for _ in (1...testCount) {
let randomNum = Int(arc4random_uniform(UInt32.max))
let randomLength = Int(arc4random_uniform(10))
let key = randomAlphaNumericString(randomLength)
let key = Utils.shared.randomAlphaNumericString(withLength: randomLength)

if key != "" {
testNums.append((key, randomNum))
treeOfInts.insert(randomNum, withKey: key)
treeOfInts.insert(data: randomNum, withKey: key)
}
}

for aTest in testNums {
let data = treeOfInts.find(aTest.key)
let data = treeOfInts.find(key: aTest.key)
if data == nil {
print("TEST FAILED. Key: \(aTest.key) Data: \(aTest.data)")
}
Expand Down
48 changes: 23 additions & 25 deletions Ternary Search Tree/TST.playground/Sources/TernarySearchTree.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,42 +18,41 @@ public class TernarySearchTree<Element> {
//MARK: - Insertion

public func insert(data: Element, withKey key: String) -> Bool {
return insertNode(&root, withData: data, andKey: key, atIndex: 0)

return insert(node: &root, withData: data, andKey: key, atIndex: 0)
}

private func insertNode(inout aNode: TSTNode<Element>?, withData data: Element, andKey key: String, atIndex charIndex: Int) -> Bool {
private func insert(node: inout TSTNode<Element>?, withData data: Element, andKey key: String, atIndex charIndex: Int) -> Bool {

//sanity check.
if key.characters.count == 0 {
return false
}

//create a new node if necessary.
if aNode == nil {
let index = key.startIndex.advancedBy(charIndex)
aNode = TSTNode<Element>(key: key[index])
if node == nil {
let index = key.index(key.startIndex, offsetBy: charIndex)
node = TSTNode<Element>(key: key[index])
}

//if current char is less than the current node's char, go left
let index = key.startIndex.advancedBy(charIndex)
if key[index] < aNode!.key {
return insertNode(&aNode!.left, withData: data, andKey: key, atIndex: charIndex)
let index = key.index(key.startIndex, offsetBy: charIndex)
if key[index] < node!.key {
return insert(node: &node!.left, withData: data, andKey: key, atIndex: charIndex)
}
//if it's greater, go right.
else if key[index] > aNode!.key {
return insertNode(&aNode!.right, withData: data, andKey: key, atIndex: charIndex)
else if key[index] > node!.key {
return insert(node: &node!.right, withData: data, andKey: key, atIndex: charIndex)
}
//current char is equal to the current nodes, go middle
else {
//continue down the middle.
if charIndex + 1 < key.characters.count {
return insertNode(&aNode!.middle, withData: data, andKey: key, atIndex: charIndex + 1)
return insert(node: &node!.middle, withData: data, andKey: key, atIndex: charIndex + 1)
}
//otherwise, all done.
else {
aNode!.data = data
aNode?.hasData = true
node!.data = data
node?.hasData = true
return true
}
}
Expand All @@ -64,32 +63,31 @@ public class TernarySearchTree<Element> {


public func find(key: String) -> Element? {
return findNode(root, withKey: key, atIndex: 0)
return find(node: root, withKey: key, atIndex: 0)
}


private func findNode(aNode: TSTNode<Element>?, withKey key: String, atIndex charIndex: Int) -> Element? {
private func find(node: TSTNode<Element>?, withKey key: String, atIndex charIndex: Int) -> Element? {

//Given key does not exist in tree.
if aNode == nil {
if node == nil {
return nil
}

let index = key.startIndex.advancedBy(charIndex)
let index = key.index(key.startIndex, offsetBy: charIndex)
//go left
if key[index] < aNode!.key {
return findNode(aNode!.left, withKey: key, atIndex: charIndex)
if key[index] < node!.key {
return find(node: node!.left, withKey: key, atIndex: charIndex)
}
//go right
else if key[index] > aNode!.key {
return findNode(aNode!.right, withKey: key, atIndex: charIndex)
else if key[index] > node!.key {
return find(node: node!.right, withKey: key, atIndex: charIndex)
}
//go middle
else {
if charIndex + 1 < key.characters.count {
return findNode(aNode!.middle, withKey: key, atIndex: charIndex + 1)
return find(node: node!.middle, withKey: key, atIndex: charIndex + 1)
} else {
return aNode!.data
return node!.data
}
}
}
Expand Down
30 changes: 30 additions & 0 deletions Ternary Search Tree/TST.playground/Sources/Utils.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// Utils.swift
//
//
// Created by Peter Bødskov on 10/01/17.
//
//

import Foundation

public struct Utils {

public static let shared = Utils()

let allowedChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
//Random string generator from:
//http://stackoverflow.com/questions/26845307/generate-random-alphanumeric-string-in-swift/26845710
public func randomAlphaNumericString(withLength length: Int) -> String {
let allowedCharsCount = UInt32(allowedChars.characters.count)
var randomString = ""

for _ in (0..<length) {
let randomNum = Int(arc4random_uniform(allowedCharsCount))
let newCharacter = allowedChars[allowedChars.index(allowedChars.startIndex, offsetBy: randomNum)]
randomString += String(newCharacter)
}

return randomString
}
}
2 changes: 1 addition & 1 deletion Ternary Search Tree/TST.playground/contents.xcplayground
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<playground version='5.0' target-platform='osx'>
<playground version='5.0' target-platform='osx' last-migration='0810'>
<timeline fileName='timeline.xctimeline'/>
</playground>
54 changes: 27 additions & 27 deletions Ternary Search Tree/TernarySearchTree.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,52 +33,52 @@ public class TernarySearchTree<Element> {

- returns: Value indicating insertion success/failure.
*/
public func insert(data: Element, withKey key: String) -> Bool {
return insertNode(&root, withData: data, andKey: key, atIndex: 0)
@discardableResult public func insert(data: Element, withKey key: String) -> Bool {
return insert(node: &root, withData: data, andKey: key, atIndex: 0)
}

/**
Helper method for insertion that does the actual legwork. Insertion is performed recursively.

- parameter aNode: The current node to insert below.
- parameter node: The current node to insert below.
- parameter data: The data being inserted.
- parameter key: The key being used to find an insertion location for the given data
- parameter charIndex: The index of the character in the key string to use to for the next node.

- returns: Value indicating insertion success/failure.
*/
private func insertNode(inout aNode: TSTNode<Element>?, withData data: Element, andKey key: String, atIndex charIndex: Int) -> Bool {
private func insert(node: inout TSTNode<Element>?, withData data: Element, andKey key: String, atIndex charIndex: Int) -> Bool {

//sanity check.
if key.characters.count == 0 {
guard key.characters.count > 0 else {
return false
}

//create a new node if necessary.
if aNode == nil {
let index = key.startIndex.advancedBy(charIndex)
aNode = TSTNode<Element>(key: key[index])
if node == nil {
let index = key.index(key.startIndex, offsetBy: charIndex)
node = TSTNode<Element>(key: key[index])
}

//if current char is less than the current node's char, go left
let index = key.startIndex.advancedBy(charIndex)
if key[index] < aNode!.key {
return insertNode(&aNode!.left, withData: data, andKey: key, atIndex: charIndex)
let index = key.index(key.startIndex, offsetBy: charIndex)
if key[index] < node!.key {
return insert(node: &node!.left, withData: data, andKey: key, atIndex: charIndex)
}
//if it's greater, go right.
else if key[index] > aNode!.key {
return insertNode(&aNode!.right, withData: data, andKey: key, atIndex: charIndex)
else if key[index] > node!.key {
return insert(node: &node!.right, withData: data, andKey: key, atIndex: charIndex)
}
//current char is equal to the current nodes, go middle
else {
//continue down the middle.
if charIndex + 1 < key.characters.count {
return insertNode(&aNode!.middle, withData: data, andKey: key, atIndex: charIndex + 1)
return insert(node: &node!.middle, withData: data, andKey: key, atIndex: charIndex + 1)
}
//otherwise, all done.
else {
aNode!.data = data
aNode?.hasData = true
node!.data = data
node?.hasData = true
return true
}
}
Expand All @@ -95,40 +95,40 @@ public class TernarySearchTree<Element> {
- returns: The element, if found. Otherwise, nil.
*/
public func find(key: String) -> Element? {
return findNode(root, withKey: key, atIndex: 0)
return find(node: root, withKey: key, atIndex: 0)
}

/**
Helper method that performs actual legwork of find operation. Implemented recursively.

- parameter aNode: The current node being evaluated.
- parameter node: The current node being evaluated.
- parameter key: The key being used for the search.
- parameter charIndex: The index of the current char in the search key

- returns: The element, if found. Nil otherwise.
*/
private func findNode(aNode: TSTNode<Element>?, withKey key: String, atIndex charIndex: Int) -> Element? {
private func find(node: TSTNode<Element>?, withKey key: String, atIndex charIndex: Int) -> Element? {

//Given key does not exist in tree.
if aNode == nil {
guard let node = node else {
return nil
}

let index = key.startIndex.advancedBy(charIndex)
let index = key.index(key.startIndex, offsetBy: charIndex)
//go left
if key[index] < aNode!.key {
return findNode(aNode!.left, withKey: key, atIndex: charIndex)
if key[index] < node.key {
return find(node: node.left, withKey: key, atIndex: charIndex)
}
//go right
else if key[index] > aNode!.key {
return findNode(aNode!.right, withKey: key, atIndex: charIndex)
else if key[index] > node.key {
return find(node: node.right, withKey: key, atIndex: charIndex)
}
//go middle
else {
if charIndex + 1 < key.characters.count {
return findNode(aNode!.middle, withKey: key, atIndex: charIndex + 1)
return find(node: node.middle, withKey: key, atIndex: charIndex + 1)
} else {
return aNode!.data
return node.data
}
}
}
Expand Down
22 changes: 22 additions & 0 deletions Ternary Search Tree/Tests/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string>
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleVersion</key>
<string>1</string>
</dict>
</plist>
Loading

0 comments on commit 53b8771

Please sign in to comment.