diff --git a/array.ts b/array.ts index 6308a46..1dc8346 100644 --- a/array.ts +++ b/array.ts @@ -2,7 +2,6 @@ import * as _ from 'lodash' interface arrayType { - _array : Array length() : number get(index: number) : any append(item: any) : arrayType @@ -16,8 +15,6 @@ const array = () => { const prototype: arrayType = { - _array: [], - length() { return this._array.length }, @@ -63,7 +60,7 @@ const array = () => { } } - return Object.create(prototype) + return Object.assign(Object.create(prototype), { _array: [] }) } diff --git a/bst.ts b/bst.ts new file mode 100644 index 0000000..3317f28 --- /dev/null +++ b/bst.ts @@ -0,0 +1,195 @@ + +interface binaryTreeNode { + minValue(): any, +} + +interface binarySearchTree { + root() : binaryTreeNode, + add(item: any) : binarySearchTree, + search(item: any) : boolean, + remove(item: any) : binarySearchTree, + preOrderTraversal(fn: (item: binaryTreeNode) => void) : void, + inOrderTraversal(fn: (item: binaryTreeNode) => void) : void, + postOrderTraversal(fn: (item: binaryTreeNode) => void) : void, + size() : number, + isEqual(node: binarySearchTree): boolean +} + +const binaryTreeNode = (item, leftItem?, rightItem?) => { + + const prototype: binaryTreeNode = { + minValue() { + if (this.left === null) { + return this._data + } else { + return this.left.minValue() + } + }, + } + + return Object.assign(Object.create(prototype), { + _data: item, + left: leftItem || null, + right: rightItem || null + }) +} + +const tree = () => { + + const prototype: binarySearchTree = { + + root() { + return this._root + }, + + add(item) { + const node = binaryTreeNode(item) + if (!this.root()) { + this._root = node + return this + } + + let current = this.root() + + while (current) { + if (node._data < current._data) { + if (!current.left) { + current.left = node + break + } + current = current.left + } else if (node._data > current._data) { + if (!current.right) { + current.right = node + break + } + current = current.right + } else { + break + } + } + return this + }, + + search(item) { + const node = binaryTreeNode(item) + + let current = this.root() + while (current) { + if (current._data === node._data) { + return true + } + if (current._data < node._data) { + current = current.right + } else { + current = current.left + } + } + return false + }, + + remove(item) { + const _remove = (node, item) => { + if (!node) { + return null + } + if (node._data === item) { + + if (!node.left && !node.right) { + return null + } + if (!node.left) { + return node.right + } + if (!node.right) { + return node.left + } + + // if the node has 2 children + const temp = node.right.minValue() + node._data = temp._data + node.right = _remove(node.right, temp) + return node + + } else if (node._data < item) { + node.right = _remove(node.right, item) + return node + } else { + node.left = _remove(node.left, item) + return node + } + } + + this._root = _remove(this.root(), item) + return this + }, + + preOrderTraversal(fn) { + const _preOrderTraversal = (node, fn) => { + if (node) { + fn(node) + _preOrderTraversal(node.left, fn) + _preOrderTraversal(node.right, fn) + } + } + _preOrderTraversal(this.root(), fn) + }, + + inOrderTraversal(fn) { + const _inOrderTraversal = (node, fn) => { + if (node) { + _inOrderTraversal(node.left, fn) + fn(node) + _inOrderTraversal(node.right, fn) + } + } + _inOrderTraversal(this.root(), fn) + }, + + postOrderTraversal(fn) { + const _postOrderTraversal = (node, fn) => { + if (node) { + _postOrderTraversal(node.left, fn) + _postOrderTraversal(node.right, fn) + fn(node) + } + } + _postOrderTraversal(this.root(), fn) + }, + + size() { + let size = 0 + this.preOrderTraversal(() => { size++ }) + return size + }, + + isEqual(other) { + const _equal = (x, y) => { + if (!x || !y) { + return false + } + if (x._data === y._data) { + if (!x.left && !y.left && !x.right && !y.right) { + return true + } else if (x.left && y.left && x.right && y.right) { + return _equal(x.left, y.left) && _equal(x.right, y.right) + } else if (x.left && y.left && !x.right && !y.right) { + return _equal(x.left, y.left) + } else if (!x.left && !y.left && x.right && y.right) { + return _equal(x.right, y.right) + } else { + return false + } + } else { + return false + } + } + return _equal(this.root(), other.root()) + } + } + + return Object.assign(Object.create(prototype), { _root: null }) + +} + +export default tree diff --git a/graph.ts b/graph.ts new file mode 100644 index 0000000..e44f835 --- /dev/null +++ b/graph.ts @@ -0,0 +1,103 @@ + +import * as _ from 'lodash' + +interface graphType { + addNode(item: any): void + removeNode(item: any): void + addEdge(n1: any, n2: any): void + removeEdge(n1: any, n2: any): void + traverseDFS(node: any, fn: (item: any) => void ): void + traverseBFS(node: any, fn: (item: any) => void ): void +} + +const graph = () => { + const prototype: graphType = { + + addNode(item) { + this.nodes.push(item) + this.edges[item] = [] + }, + + removeNode(item) { + this.nodes = this.nodes.filter(node => { + return node != item + }) + + while (this.edges[item].length) { + let adjacentNode = this.edges[item].pop() + this.removeEdge(item, adjacentNode) + } + }, + + addEdge(n1, n2) { + this.edges[n1].push(n2) + this.edges[n2].push(n1) + this.numberOfEdges++ + }, + + removeEdge(n1, n2) { + if (this.edges[n1]) { + this.edges[n1] = this.edges[n1].filter(node => { + return node != n2 + }) + } + if (this.edges[n2]) { + this.edges[n2] = this.edges[n2].filter(node => { + return node != n1 + }) + } + }, + + traverseDFS(node, fn) { + + if (this.nodes.indexOf(node) === -1) { + throw new Error('Node not in graph') + } + + const _dfs = (node, visitedNodes, fn) => { + visitedNodes[node] = true + if (this.edges[node] !== undefined) { + fn(node) + } + this.edges[node].forEach(adjNode => { + if (!visitedNodes[adjNode]) { + this._dfs(adjNode, visitedNodes, fn) + } + }) + } + + _dfs(node, {}, fn) + + }, + + traverseBFS(node, fn) { + + if (this.nodes.indexOf(node) === -1) { + throw new Error('Node not in graph') + } + + const queue = [node] + const visitedNodes = {} + visitedNodes[node] = true + + while (queue.length) { + let currNode = queue.shift() + fn(currNode) + this.edges[currNode].forEach(adjNode => { + if (!visitedNodes[adjNode]) { + visitedNodes[adjNode] = true + queue.push(adjNode) + } + }) + } + } + } + + return Object.assign(Object.create(prototype), { + nodes: [], + edges: {}, + numberOfEdges: 0 + }) +} + +export default graph diff --git a/queue.ts b/queue.ts new file mode 100644 index 0000000..a4a824c --- /dev/null +++ b/queue.ts @@ -0,0 +1,38 @@ + +interface queueType { + length(): number + enqueue(item: any): queueType + dequeue(item: any): any + peek(): any +} + +const queue = () => { + const prototype: queueType = { + length() { + return this._queue.length + }, + + enqueue(item) { + this._queue.push(item) + return this + }, + + dequeue(item) { + if (this.length() <= 0) { + throw new Error('Queue is empty') + } + return this._queue.shift() + }, + + peek() { + if (this.length() <= 0) { + throw new Error('Queue is empty') + } + return this._queue[0] + } + } + + return Object.assign(Object.create(prototype), { _queue: [] }) +} + +export default queue diff --git a/stack.js b/stack.js deleted file mode 100644 index 188577e..0000000 --- a/stack.js +++ /dev/null @@ -1,11 +0,0 @@ -/* - -Array-based Stack - - */ -"use strict"; -exports.stack = function () { - const prototype = { - _stack: [] - }; -}; diff --git a/stack.ts b/stack.ts index dd73884..8840883 100644 --- a/stack.ts +++ b/stack.ts @@ -8,20 +8,17 @@ Array-based Stack import * as _ from 'lodash' interface stackType { - _stack : Array, size() : number, push(item: any) : stackType, pop(item: any) : any, peek() : any, - isEqual(item: stackType) : boolean + isEqual(any) : boolean } const stack = () => { const prototype: stackType = { - _stack: [], - size() { return this._stack.length }, @@ -53,7 +50,7 @@ const stack = () => { } - return Object.create(prototype) + return Object.assign(Object.create(prototype), { _stack: [] }) } diff --git a/test/bst-test.js b/test/bst-test.js new file mode 100644 index 0000000..3fc036d --- /dev/null +++ b/test/bst-test.js @@ -0,0 +1,72 @@ + +import tree from '../js/bst' +import test from 'ava' + +test('Tree size', t => { + const x = tree() + t.is(x.size(), 0) + x.add(1) + t.is(x.size(), 1) + + x.add(2).add(3) + + t.is(x.size(), 3) +}) + +test('Tree add/isEqual', t => { + const x = tree() + const y = tree() + + x.add(1).add(2) + y.add(1).add(2) + + t.ok(x.isEqual( + y + )) + + y.add(3) + + t.notOk(x.isEqual( + y + )) +}) + +test('Tree remove', t => { + const x = tree() + x.add(1).add(2).add(4).add(15).add(3) + + x.remove(15) + t.is(x.size(), 4) + + x.remove(5) + t.is(x.size(), 4) + + x.remove(3) + t.is(x.size(), 3) +}) + +test('Tree search', t => { + const x = tree() + x.add(1).add(2) + + t.ok(x.search(2)) + t.notOk(x.search(3)) +}) + +// not a full test of the correct ordering +// but at least tests that the traversal visits all nodes +test('Tree traversals', t => { + const x = tree() + const y = tree() + + x.add(1).add(2).add(3).add(4) + y.add(1).add(2).add(3).add(4) + + let size = 0 + x.inOrderTraversal(() => size++) + t.is(size, y.size()) + + size = 0 + x.postOrderTraversal(() => size++) + t.is(size, y.size()) +}) diff --git a/test/queue-test.js b/test/queue-test.js new file mode 100644 index 0000000..80884e7 --- /dev/null +++ b/test/queue-test.js @@ -0,0 +1,26 @@ + +import queue from '../js/queue' +import test from 'ava' + +test('Queue length/enqueue', t => { + const x = queue() + t.is(x.length(), 0) + x.enqueue(1) + t.is(x.length(), 1) + x.enqueue(2).enqueue(3) + t.is(x.length(), 3) +}) + +test('Queue dequeue', t => { + const x = queue() + x.enqueue(1).enqueue(2) + t.is(x.length(), 2) + t.is(x.dequeue(), 1) + t.is(x.length(), 1) +}) + +test('Queue peek', t => { + const x = queue() + x.enqueue(1).enqueue(2) + t.is(x.peek(), 1) +}) \ No newline at end of file diff --git a/test2.js b/test2.js new file mode 100644 index 0000000..fa368f5 --- /dev/null +++ b/test2.js @@ -0,0 +1,109 @@ +var proto = { + a: function() { + return this.b + }, + c() { + return this.b + } +} + +x = Object.assign(Object.create(proto), {b: 2}) +console.log(x) +console.log(x.a()) +console.log(x.c()) + +var thing = { + data: 1, + left: { + data: 2, + left: 5, + right: { + data: 10, + left: null, + right: { + data: 12, + left: null, + right: { + data: 14, + left: { + data: 12, + left: null, + right: null + }, + right: { + data: 14, + left: null, + right: null + } + } + } + } + }, + right: { + data: 1, + left: null, + right: null + } +} + +var thing2 = { + data: 1, + left: { + data: 2, + left: 5, + right: { + data: 10, + left: null, + right: { + data: 12, + left: null, + right: { + data: 14, + left: { + data: 12, + left: null, + right: null + }, + right: { + data: 14, + left: null, + right: null + } + } + } + } + }, + right: { + data: 1, + left: null, + right: null + } +} + +var isEqual = function(x, y) { + + if (!x || !y) { + return false + } + + if (x.data === y.data) { + + if (!x.left && !y.left && !x.right && !y.right) { + return true + } else if (x.left && y.left && x.right && y.right) { + return isEqual(x.left, y.left) && isEqual(x.right, y.right) + } else if (x.left && y.left && !x.right && !y.right) { + return isEqual(x.left, y.left) + } else if (!x.left && !y.left && x.right && y.right) { + return isEqual(x.right, y.right) + } else { + return false + } + + } else { + return false + } + +} + +console.log(isEqual(thing, thing2)); \ No newline at end of file