Skip to content

Commit

Permalink
Add general-purpose tree
Browse files Browse the repository at this point in the history
  • Loading branch information
hollance committed Feb 3, 2016
1 parent 6deb02d commit 776269f
Show file tree
Hide file tree
Showing 14 changed files with 304 additions and 1 deletion.
2 changes: 1 addition & 1 deletion README.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ Often just using the built-in `Array`, `Dictionary`, and `Set` types is sufficie

### Trees

- Tree (general-purpose)
- [Tree](Tree/). A general-purpose tree structure.
- Binary Tree
- Binary Search Tree (BST)
- AVL Tree
Expand Down
Binary file added Tree/Images/Cycles.graffle
Binary file not shown.
Binary file added Tree/Images/Cycles.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Tree/Images/Example.graffle
Binary file not shown.
Binary file added Tree/Images/Example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Tree/Images/ParentChildren.graffle
Binary file not shown.
Binary file added Tree/Images/ParentChildren.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added Tree/Images/Tree.graffle
Binary file not shown.
Binary file added Tree/Images/Tree.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
168 changes: 168 additions & 0 deletions Tree/README.markdown
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
# Tree

A tree represents hierarchical relationships between objects. This is a tree:

![A tree](Images/Tree.png)

A tree consists of nodes.

Nodes have links to their children and usually to their parent as well. The children are the nodes below a given node; the parent is the node above. A node always has just one parent but can have multiple children.

![A tree](Images/ParentChildren.png)

A node without a parent is the *root* node. A node without children is a *leaf* node.

The pointers in a tree do not form cycles. This is not a tree:

![Not a tree](Images/Cycles.png)

Such a structure is called a [graph](../Graph/). A tree is really a very simple form of a graph. (In a similar vein, a [linked list](../Linked List) is a simple version of a tree. Think about it!)

This article talks about a general-purpose tree. That's a tree without any kind of restrictions on how many children each node may have, or on the order of the nodes in the tree.

Here's a basic implementation in Swift:

```swift
public class TreeNode<T> {
public var value: T

public var parent: TreeNode?
public var children = [TreeNode<T>]()

public init(value: T) {
self.value = value
}

func addChild(node: TreeNode<T>) {
children.append(node)
node.parent = self
}
}
```

This describes a single node from the tree. It has a value of generic type `T`, a reference to a `parent` node, and an array of child nodes.

It will be useful to add a `description` method so you can print the tree:

```swift
extension TreeNode: CustomStringConvertible {
public var description: String {
var s = "\(value)"
if !children.isEmpty {
s += " {" + children.map { $0.description }.joinWithSeparator(", ") + "}"
}
return s
}
}
```

To see this in action in a playground:

```swift
let tree = TreeNode<String>(value: "beverages")

let hotNode = TreeNode<String>(value: "hot")
let coldNode = TreeNode<String>(value: "cold")

let teaNode = TreeNode<String>(value: "tea")
let coffeeNode = TreeNode<String>(value: "coffee")
let chocolateNode = TreeNode<String>(value: "cocoa")

let blackTeaNode = TreeNode<String>(value: "black")
let greenTeaNode = TreeNode<String>(value: "green")
let chaiTeaNode = TreeNode<String>(value: "chai")

let sodaNode = TreeNode<String>(value: "soda")
let milkNode = TreeNode<String>(value: "milk")

let gingerAleNode = TreeNode<String>(value: "ginger ale")
let bitterLemonNode = TreeNode<String>(value: "bitter lemon")

tree.addChild(hotNode)
tree.addChild(coldNode)

hotNode.addChild(teaNode)
hotNode.addChild(coffeeNode)
hotNode.addChild(chocolateNode)

coldNode.addChild(sodaNode)
coldNode.addChild(milkNode)

teaNode.addChild(blackTeaNode)
teaNode.addChild(greenTeaNode)
teaNode.addChild(chaiTeaNode)

sodaNode.addChild(gingerAleNode)
sodaNode.addChild(bitterLemonNode)
```

If you print out the value of `tree`, you'll get:

beverages {hot {tea {black, green, chai}, coffee, cocoa}, cold {soda {ginger ale, bitter lemon}, milk}}

That corresponds to the following structure:

![Example tree](Images/Example.png)

The `beverages` node is the root because it has no parent. The leaves are `black`, `green`, `chai`, `coffee`, `cocoa`, `ginger ale`, `bitter lemon`, `milk` because they don't have any child nodes.

For any node you can look at the `parent` property and work your way back up to the root:

```swift
teaNode.parent // this is the "hot" node
teaNode.parent!.parent // this is the root
```

We often use the following definitions when talking about trees:

- **Height of the tree.** This is the number of links between the root node and the lowest leaf. In our example the height of the tree is 3 because it takes three jumps to go from the root to the bottom.

- **Depth of a node.** The number of links between this node and the root node. For example, the depth of `tea` is 2 because it takes two jumps to reach the root. (The root itself has depth 0.)

There are many different ways to construct trees. For example, sometimes you don't need to have a `parent` property at all. Or maybe you only need to give each node a maximum of two children -- such a tree is called a [binary tree](../Binary Tree/). A very common type of tree is the [binary search tree](../Binary Search Tree/) (or BST), a stricter version of a binary tree where the nodes are ordered in a particular way to speed up searches.

The general purpose tree I've shown here is great for describing hierarchical data, but it really depends on your application what kind of extra functionality it needs to have.

Here's an example of how you could use the `TreeNode` class to determine if the tree contains a particular value. You first look at the node's own `value` property. If there's no match, then you look at all your children in turn. Of course, those children are also `TreeNodes` so they will repeat the same process recursively: first look at their own value and then at their children. And their children will also do the same thing again, and so on... Recursion and trees go hand-in-hand.

Here's the code:

```swift
extension TreeNode where T: Equatable {
func search(value: T) -> TreeNode? {
if value == self.value {
return self
}
for child in children {
if let found = child.search(value) {
return found
}
}
return nil
}
}
```

And an example of how to use this:

```swift
tree.search("cocoa") // returns the "cocoa" node
tree.search("chai") // returns the "chai" node
tree.search("bubbly") // nil
```

It's also possible to describe a tree using nothing more than an array. The indices in the array then create the links between the different nodes. For example, if we have:

0 = beverage 5 = cocoa 9 = green
1 = hot 6 = soda 10 = chai
2 = cold 7 = milk 11 = ginger ale
3 = tea 8 = black 12 = bitter lemon
4 = coffee

Then we can describe the tree with the following array:

[ -1, 0, 0, 1, 1, 1, 2, 2, 3, 3, 3, 6, 6 ]

Each entry in the array is a pointer to its parent node. The item at index 3, `tea`, has the value 1 because its parent is `hot`. The root node points to `-1` because it has no parent. You can only traverse such trees from a node back to the root but not the other way around.

*Written for Swift Algorithm Club by Matthijs Hollemans*
86 changes: 86 additions & 0 deletions Tree/Tree.playground/Contents.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//: Playground - noun: a place where people can play

public class TreeNode<T> {
public var value: T

public var parent: TreeNode?
public var children = [TreeNode<T>]()

public init(value: T) {
self.value = value
}

func addChild(node: TreeNode<T>) {
children.append(node)
node.parent = self
}
}

extension TreeNode: CustomStringConvertible {
public var description: String {
var s = "\(value)"
if !children.isEmpty {
s += " {" + children.map { $0.description }.joinWithSeparator(", ") + "}"
}
return s
}
}

let tree = TreeNode<String>(value: "beverages")

let hotNode = TreeNode<String>(value: "hot")
let coldNode = TreeNode<String>(value: "cold")

let teaNode = TreeNode<String>(value: "tea")
let coffeeNode = TreeNode<String>(value: "coffee")
let chocolateNode = TreeNode<String>(value: "cocoa")

let blackTeaNode = TreeNode<String>(value: "black")
let greenTeaNode = TreeNode<String>(value: "green")
let chaiTeaNode = TreeNode<String>(value: "chai")

let sodaNode = TreeNode<String>(value: "soda")
let milkNode = TreeNode<String>(value: "milk")

let gingerAleNode = TreeNode<String>(value: "ginger ale")
let bitterLemonNode = TreeNode<String>(value: "bitter lemon")

tree.addChild(hotNode)
tree.addChild(coldNode)

hotNode.addChild(teaNode)
hotNode.addChild(coffeeNode)
hotNode.addChild(chocolateNode)

coldNode.addChild(sodaNode)
coldNode.addChild(milkNode)

teaNode.addChild(blackTeaNode)
teaNode.addChild(greenTeaNode)
teaNode.addChild(chaiTeaNode)

sodaNode.addChild(gingerAleNode)
sodaNode.addChild(bitterLemonNode)

tree

teaNode.parent
teaNode.parent!.parent

extension TreeNode where T: Equatable {
func search(value: T) -> TreeNode? {
if value == self.value {
return self
}
for child in children {
if let found = child.search(value) {
return found
}
}
return nil
}
}

tree.search("cocoa")
tree.search("chai")
tree.search("bubbly")
4 changes: 4 additions & 0 deletions Tree/Tree.playground/contents.xcplayground
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<playground version='5.0' target-platform='osx'>
<timeline fileName='timeline.xctimeline'/>
</playground>
6 changes: 6 additions & 0 deletions Tree/Tree.playground/timeline.xctimeline
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Timeline
version = "3.0">
<TimelineItems>
</TimelineItems>
</Timeline>
39 changes: 39 additions & 0 deletions Tree/Tree.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
public class TreeNode<T> {
public var value: T

public var parent: TreeNode?
public var children = [TreeNode<T>]()

public init(value: T) {
self.value = value
}

func addChild(node: TreeNode<T>) {
children.append(node)
node.parent = self
}
}

extension TreeNode: CustomStringConvertible {
public var description: String {
var s = "\(value)"
if !children.isEmpty {
s += " {" + children.map { $0.description }.joinWithSeparator(", ") + "}"
}
return s
}
}

extension TreeNode where T: Equatable {
func search(value: T) -> TreeNode? {
if value == self.value {
return self
}
for child in children {
if let found = child.search(value) {
return found
}
}
return nil
}
}

0 comments on commit 776269f

Please sign in to comment.