Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ script:
- 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/Tests/Tests.xcodeproj -scheme Tests
# - xcodebuild test -project ./Binary\ Search\ Tree/Solution\ 1/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
Expand Down
75 changes: 39 additions & 36 deletions Binary Search Tree/README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ Sometimes you don't want to look at just a single node, but at all of them.
There are three ways to traverse a binary tree:

1. *In-order* (or *depth-first*): first look at the left child of a node, then at the node itself, and finally at its right child.
2. *Pre-order*: first look at a node, then its left and right children.
2. *Pre-order*: first look at a node, then its left and right children.
3. *Post-order*: first look at the left and right children and process the node itself last.

Once again, this happens recursively.
Expand Down Expand Up @@ -133,7 +133,7 @@ public class BinarySearchTree<T: Comparable> {
}
```

This class describes just a single node, not the entire tree. It's a generic type, so the node can store any kind of data. It also has references to its `left` and `right` child nodes and a `parent` node.
This class describes just a single node, not the entire tree. It's a generic type, so the node can store any kind of data. It also has references to its `left` and `right` child nodes and a `parent` node.

Here's how you'd use it:

Expand Down Expand Up @@ -258,7 +258,7 @@ Here is the implementation of `search()`:

I hope the logic is clear: this starts at the current node (usually the root) and compares the values. If the search value is less than the node's value, we continue searching in the left branch; if the search value is greater, we dive into the right branch.

Of course, if there are no more nodes to look at -- when `left` or `right` is nil -- then we return `nil` to indicate the search value is not in the tree.
Of course, if there are no more nodes to look at -- when `left` or `right` is nil -- then we return `nil` to indicate the search value is not in the tree.

> **Note:** In Swift that's very conveniently done with optional chaining; when you write `left?.search(value)` it automatically returns nil if `left` is nil. There's no need to explicitly check for this with an `if` statement.
Expand Down Expand Up @@ -300,21 +300,21 @@ The first three lines all return the corresponding `BinaryTreeNode` object. The
Remember there are 3 different ways to look at all nodes in the tree? Here they are:

```swift
public func traverseInOrder(@noescape process: T -> Void) {
left?.traverseInOrder(process)
public func traverseInOrder(process: (T) -> Void) {
left?.traverseInOrder(process: process)
process(value)
right?.traverseInOrder(process)
right?.traverseInOrder(process: process)
}
public func traversePreOrder(@noescape process: T -> Void) {

public func traversePreOrder(process: (T) -> Void) {
process(value)
left?.traversePreOrder(process)
right?.traversePreOrder(process)
left?.traversePreOrder(process: process)
right?.traversePreOrder(process: process)
}
public func traversePostOrder(@noescape process: T -> Void) {
left?.traversePostOrder(process)
right?.traversePostOrder(process)

public func traversePostOrder(process: (T) -> Void) {
left?.traversePostOrder(process: process)
right?.traversePostOrder(process: process)
process(value)
}
```
Expand All @@ -339,11 +339,12 @@ This prints the following:
You can also add things like `map()` and `filter()` to the tree. For example, here's an implementation of map:

```swift
public func map(@noescape formula: T -> T) -> [T] {

public func map(formula: (T) -> T) -> [T] {
var a = [T]()
if let left = left { a += left.map(formula) }
if let left = left { a += left.map(formula: formula) }
a.append(formula(value))
if let right = right { a += right.map(formula) }
if let right = right { a += right.map(formula: formula) }
return a
}
```
Expand All @@ -365,7 +366,7 @@ tree.toArray() // [1, 2, 5, 7, 9, 10]
```

As an exercise for yourself, see if you can implement filter and reduce.

### Deleting nodes

We can make the code much more readable by defining some helper functions.
Expand Down Expand Up @@ -395,7 +396,7 @@ We also need a function that returns the minimum and maximum of a node:
}
return node
}

public func maximum() -> BinarySearchTree {
var node = self
while case let next? = node.right {
Expand All @@ -411,30 +412,32 @@ The rest of the code is pretty self-explanatory:
```swift
@discardableResult public func remove() -> BinarySearchTree? {
let replacement: BinarySearchTree?

// Replacement for current node can be either biggest one on the left or
// smallest one on the right, whichever is not nil
if let left = left {
replacement = left.maximum()
} else if let right = right {
if let right = right {
replacement = right.minimum()
} else if let left = left {
replacement = left.maximum()
} else {
replacement = nil;
replacement = nil
}
replacement?.remove();

replacement?.remove()

// Place the replacement on current node's position
replacement?.right = right;
replacement?.left = left;
reconnectParentTo(node:replacement);

replacement?.right = right
replacement?.left = left
right?.parent = replacement
left?.parent = replacement
reconnectParentTo(node:replacement)

// The current node is no longer part of the tree, so clean it up.
parent = nil
left = nil
right = nil
return replacement;

return replacement
}
```

Expand Down Expand Up @@ -574,7 +577,7 @@ if let node1 = tree.search(1) {

## The code (solution 2)

We've implemented the binary tree node as a class but you can also use an enum.
We've implemented the binary tree node as a class but you can also use an enum.

The difference is reference semantics versus value semantics. Making a change to the class-based tree will update that same instance in memory. But the enum-based tree is immutable -- any insertions or deletions will give you an entirely new copy of the tree. Which one is best totally depends on what you want to use it for.

Expand All @@ -588,7 +591,7 @@ public enum BinarySearchTree<T: Comparable> {
}
```

The enum has three cases:
The enum has three cases:

- `Empty` to mark the end of a branch (the class-based version used `nil` references for this).
- `Leaf` for a leaf node that has no children.
Expand All @@ -606,7 +609,7 @@ As usual, we'll implement most functionality recursively. We'll treat each case
case let .Node(left, _, right): return left.count + 1 + right.count
}
}

public var height: Int {
switch self {
case .Empty: return 0
Expand All @@ -623,7 +626,7 @@ Inserting new nodes looks like this:
switch self {
case .Empty:
return .Leaf(newValue)

case .Leaf(let value):
if newValue < value {
return .Node(.Leaf(newValue), value, .Empty)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public class BinarySearchTree<T: Comparable> {
public convenience init(array: [T]) {
precondition(array.count > 0)
self.init(value: array.first!)
for v in array.dropFirst() {
for v in array.dropFirst() {
insert(value: v)
}
}
Expand Down Expand Up @@ -106,30 +106,32 @@ extension BinarySearchTree {
*/
@discardableResult public func remove() -> BinarySearchTree? {
let replacement: BinarySearchTree?

// Replacement for current node can be either biggest one on the left or
// smallest one on the right, whichever is not nil
if let left = left {
replacement = left.maximum()
} else if let right = right {
if let right = right {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the significance of handling the right child before the left?

Copy link
Contributor Author

@weihanglo weihanglo Feb 5, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The previous version before 942e3b1 use successor to replace the node with 2 children. The unit tests are also based on this logic. I am kind of lazy to modify all the test cases 😵.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Alright cool. I think this checks out. Merging!

replacement = right.minimum()
} else if let left = left {
replacement = left.maximum()
} else {
replacement = nil;
replacement = nil
}
replacement?.remove();

replacement?.remove()

// Place the replacement on current node's position
replacement?.right = right;
replacement?.left = left;
reconnectParentTo(node:replacement);

replacement?.right = right
replacement?.left = left
right?.parent = replacement
left?.parent = replacement
reconnectParentTo(node:replacement)

// The current node is no longer part of the tree, so clean it up.
parent = nil
left = nil
right = nil
return replacement;

return replacement
}

private func reconnectParentTo(node: BinarySearchTree?) {
Expand Down Expand Up @@ -322,7 +324,7 @@ extension BinarySearchTree: CustomStringConvertible {
}
return s
}

public func toArray() -> [T] {
return map { $0 }
}
Expand Down
Loading