Skip to content

Commit

Permalink
Adds Tree
Browse files Browse the repository at this point in the history
  • Loading branch information
choskim committed Feb 23, 2015
1 parent 409e669 commit ce92334
Show file tree
Hide file tree
Showing 4 changed files with 312 additions and 2 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,12 @@ In this repository, you'll find the following data structures:
- [Queue][3]
- [Singly-Linked List][4]
- [Doubly-Linked List][5]
- Tree
- [Tree][6]

[1]: http://tutsplus.com/

[2]: /src/stack.js
[3]: /src/queue.js
[4]: /src/singly_list.js
[5]: /src/doubly_list.js
[5]: /src/doubly_list.js
[6]: /src/tree.js
144 changes: 144 additions & 0 deletions spec/tree_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
describe("Tree", function() {
var tree = null;

beforeEach(function() {
tree = new Tree(1);
tree._root.children.push(new Node(2));
tree._root.children[0].parent = tree;
tree._root.children.push(new Node(3));
tree._root.children[1].parent = tree;
tree._root.children.push(new Node(4));
tree._root.children[2].parent = tree;

tree._root.children[0].children.push(new Node(5));
tree._root.children[0].children[0].parent = tree._root.children[0];

tree._root.children[0].children.push(new Node(6));
tree._root.children[0].children[1].parent = tree._root.children[0];

tree._root.children[2].children.push(new Node(7));
tree._root.children[2].children[0].parent = tree._root.children[2];

// 1
// / | \
// 2 3 4
// / \ |
// 5 6 7
});

it("should have 1 as root's data", function() {
expect(tree._root.data).toEqual(1);
});

it("should have null as root's parent", function() {
expect(tree._root.parent).toEqual(null);
});

it("should have [] as root's children", function() {
expect(tree._root.children).toEqual(jasmine.any(Array));
});

describe("#traverseDF(callback)", function() {
var sequence = '';

beforeEach(function() {
tree.traverseDF(function(node) {
sequence += node.data;
});
});

it("should have the correct sequence", function() {
expect(sequence).toEqual("5623741");
});
});

describe("#traverseBF(callback)", function() {
var sequence = '';

beforeEach(function() {
tree.traverseBF(function(node) {
sequence += node.data;
});
});

it("should have the correct sequence", function() {
expect(sequence).toEqual("1234567");
});
});

describe("#contains(callback, traversal)", function() {
it("should return even numbers with DFS", function() {
var sequence = '';

tree.contains(function(node) {
if (!(node.data % 2)) {
sequence += node.data;
}
}, tree.traverseDF);

expect(sequence).toEqual("624");
});

it("should return even numbers with BFS", function() {
var sequence = '';

tree.contains(function(node) {
if (!(node.data % 2)) {
sequence += node.data;
}
}, tree.traverseBF);

expect(sequence).toEqual("246");
});
});

describe("#add(data, toData, traverse)", function() {
var sequence = '';

it("should add node with a valid data of 8", function() {
tree.add(8, 4, tree.traverseBF);
tree.traverseBF(function(node) {
sequence += node.data;
});

expect(sequence).toEqual("12345678");
});

it("should throw an error with an invalid parent", function() {
expect(function() {
tree.add(9, 400, tree.traverseBF);
}).toThrowError("Cannot add node to a non-existent parent.");
});
});

describe("#remove(data, toData, traverse)", function() {
var sequence = '';

beforeEach(function() {
tree.add(8, 4, tree.traverseBF);
});

it("should remove node with a valid data of 8", function() {
tree.remove(8, 4, tree.traverseBF);
tree.traverseBF(function(node) {
sequence += node.data;
});

expect(sequence).toEqual("1234567");
});

it("should throw an error with an invalid parent", function() {
expect(function() {
tree.remove(8, 400, tree.traverseBF);
}).toThrowError("Parent does not exist.");
});

it("should throw an error with an invalid node", function() {
expect(function() {
tree.remove(80, 4, tree.traverseBF);
}).toThrowError("Node to remove does not exist.");
});
});
});


20 changes: 20 additions & 0 deletions spec_runner/tree.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Jasmine Spec Runner v2.2.0</title>

<link rel="shortcut icon" type="image/png" href="../lib/jasmine-2.2.0/jasmine_favicon.png">
<link rel="stylesheet" href="../lib/jasmine-2.2.0/jasmine.css">

<script src="../lib/jasmine-2.2.0/jasmine.js"></script>
<script src="../lib/jasmine-2.2.0/jasmine-html.js"></script>
<script src="../lib/jasmine-2.2.0/boot.js"></script>

<script src="../src/tree.js"></script>
<script src="../spec/tree_spec.js"></script>
</head>

<body>
</body>
</html>
145 changes: 145 additions & 0 deletions src/tree.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
function Node(data) {
this.data = data;
this.parent = null;
this.children = [];
}

function Tree(data) {
var node = new Node(data);
this._root = node;
}

Tree.prototype.traverseDF = function(callback) {

// this is a recursive and immediately-invoking function
(function recurse(currentNode) {

// step 2
for (var i = 0, length = currentNode.children.length; i < length; i++) {

// step 3
recurse(currentNode.children[i]);
}

// step 4
callback(currentNode);

// step 1
})(this._root);

};

Tree.prototype.traverseBF = function(callback) {
var queue = new Queue();
queue.enqueue(this._root);

currentTree = queue.dequeue();

while(currentTree){
for (var i = 0, length = currentTree.children.length; i < length; i++) {
queue.enqueue(currentTree.children[i]);
}

callback(currentTree);
currentTree = queue.dequeue();
}
};

Tree.prototype.contains = function(callback, traversal) {
traversal.call(this, callback);
};

Tree.prototype.add = function(data, toData, traversal) {
var child = new Node(data),
parent = null,
callback = function(node) {
if (node.data === toData) {
parent = node;
}
};

this.contains(callback, traversal);

if (!parent) {
throw new Error("Cannot add node to a non-existent parent.");
} else {
parent.children.push(child);
child.parent = parent;
}
};

Tree.prototype.remove = function(data, fromData, traversal) {
var tree = this,
parent = null,
childToRemove = null,
index;

var callback = function(node) {
if (node.data === fromData) {
parent = node;
}
};

this.contains(callback, traversal);

if (!parent) {
throw new Error("Parent does not exist.");

} else {
index = findIndex(parent.children, data);

if (index === undefined) {
throw new Error("Node to remove does not exist.");
} else {
childToRemove = parent.children.splice(index, 1);
}

}

return childToRemove;
};

// Helper Method of Tree
function findIndex(arr, data) {
var index;

for (var i = 0; i < arr.length; i++) {
if (arr[i].data === data) {
index = i;
}
}

return index;
}

// Queue Constructor
function Queue() {
this._oldestIndex = 1;
this._newestIndex = 1;
this._storage = {};
}

Queue.prototype.size = function() {
return this._newestIndex - this._oldestIndex;
};

Queue.prototype.enqueue = function(data) {
this._storage[this._newestIndex] = data;
this._newestIndex++;
};

Queue.prototype.dequeue = function() {
var oldestIndex = this._oldestIndex,
newestIndex = this._newestIndex,
deletedData;

if (oldestIndex === newestIndex) {
return;
} else {
deletedData = this._storage[oldestIndex];
delete this._storage[oldestIndex];
this._oldestIndex++;

return deletedData;
}
};

0 comments on commit ce92334

Please sign in to comment.