diff --git a/spec/binarySearchTree_test.js b/spec/binarySearchTree_test.js new file mode 100644 index 0000000..df3b9b7 --- /dev/null +++ b/spec/binarySearchTree_test.js @@ -0,0 +1,55 @@ +import chai, { expect } from 'chai' +import chaiChange from 'chai-change' +import BinarySearchTree from '../src/binarySearchTree' + +chai.use(chaiChange) + +describe.only('Binary Search Tree', () => { + 'use strict' + + it('exists', () => { + expect(BinarySearchTree).to.be.a('function') + }) + + describe('insert()', () => { + it('inserts a node with the specified value into the tree.', () => { + const bst = new BinarySearchTree() + bst.insert(50) + bst.insert(30) + bst.insert(51) + bst.insert(31) + // bst.traverse( (val) => console.log('hello') ) + expect(() => bst.insert(3)) + .to.alter(() => bst.count(), { from: 4, to: 5 }) + }) + }) + + describe('search()', () => { + const bst = new BinarySearchTree() + bst.insert(50) + bst.insert(30) + bst.insert(51) + bst.insert(31) + + + it('returns a node object if found.', () => { + expect(bst.search(51)).to.eql({ value: 51, left: null, right: null }) + }) + + it('returns null if not found.', () => { + expect(bst.search(3)).to.be.null + }) + }) + + describe('delete()', () => { + const bst = new BinarySearchTree() + bst.insert(50) + bst.insert(30) + bst.insert(51) + bst.insert(31) + bst.insert(15) + bst.remove(50) + console.log(bst) + + }) +}) diff --git a/spec/hashTable_test.js b/spec/hashTable_test.js new file mode 100644 index 0000000..8ce4e8f --- /dev/null +++ b/spec/hashTable_test.js @@ -0,0 +1,97 @@ +import chai, { expect, assert } from 'chai' +import chaiChange from 'chai-change' +import HashTable from '../src/hashTable' + +chai.use(chaiChange) + +describe('Hash Table', () => { + 'use strict' + + it('exists', () => { + expect(HashTable).to.be.a('function') + }) + + describe('put()', () => { + it('adds a key-value pair to the hash table', () => { + const hashTable = new HashTable() + + expect(() => hashTable.put("name", "Zanzibar")) + .to.alter(() => hashTable.size(), { from: 0, to: 1 }) + }) + }) + + describe('hash()', () => { + const hashTable = new HashTable() + let hashedKey = hashTable.hash("name") + + it('generates a hash for the key "name"', () => { + expect(hashTable.hash("name")).to.equal(hashedKey) + assert.isNumber(hashedKey, "hashed key is a number") + }) + }) + + describe('get()', () => { + it('returns the data associated with key.', () => { + const hashTable = new HashTable() + hashTable.put("name", "Zanzibar") + + expect(hashTable.get("name")).to.eql("Zanzibar") + }) + }) + + describe('contains()', () => { + const hashTable = new HashTable() + hashTable.put("name", "Zanzibar") + hashTable.put("place", "Zimbabwe") + hashTable.put("animal", "Zebra") + + it('returns true if the hash table contains the key.', () => { + expect(hashTable.contains("place")).to.be.true + }) + it('returns false if the hash table does not contain the key', () => { + expect(hashTable.contains("car")).to.be.false + }) + }) + + describe('iterate()', () => { + it('takes a callback function and passes it each key and value in sequence.', + () => { + const hashTable = new HashTable() + hashTable.put("name", "Zanzibar") + hashTable.put("place", "Zimbabwe") + hashTable.put("animal", "Zebra") + let keyValueArray = [] + + hashTable.iterate((k, v) => keyValueArray.push(`${k}: ${v}`)) + + expect(keyValueArray.length) + .to.equal(3) + }) + }) + + describe('remove()', () => { + it('removes a key-value pair by key.', () => { + const hashTable = new HashTable() + hashTable.put("name", "Zanzibar") + hashTable.put("place", "Zimbabwe") + hashTable.put("animal", "Zebra") + + expect(() => hashTable.remove("name")) + .to.alter(() => hashTable.size(), { from: 3, to: 2 }) + expect(hashTable.get("name")) + .to.be.null + }) + }) + + describe('size()', () => { + it('returns the number of key-value pairs in the hash table.', () => { + const hashTable = new HashTable() + hashTable.put("name", "Zanzibar") + hashTable.put("place", "Zimbabwe") + hashTable.put("animal", "Zebra") + + expect(hashTable.size()).to.equal(3) + }) + }) + +}) diff --git a/src/binarySearchTree.js b/src/binarySearchTree.js new file mode 100644 index 0000000..54d6b06 --- /dev/null +++ b/src/binarySearchTree.js @@ -0,0 +1,157 @@ +'use strict' + +class TreeNode { + constructor(value) { + this.value = value; + this.left = null; + this.right = null; + } + +} + +export default class BinarySearchTree { + constructor() { + this.root = null; + this.size = 0; + } + + insert(value) { + let node = new TreeNode(value); + let current = this.root + + if(this.root === null) { + this.root = node; + this.size++ + + } else { + while( current !== null ){ + if (node.value < current.value) { + if (current.left === null) { + current.left = node; + this.size++; + break + } else { + current = current.left + } + } else { + if (current.right === null) { + current.right = node; + this.size++; + break + } else { + current = current.right + } + } + } + } + } + + search(value) { + if (this.root === null) { + return null + } + if (this.root.value === value) { + return this.root + } + let current = this.root + while (current) { + if (current.value > value) { + if (current.left === null) { + return null + } else if (current.left.value === value) { + return current.left + } else { + current = current.left + } + } else if (current.value < value) { + if (current.right === null) { + return null + } else if (current.right.value === value) { + return current.right + } else { + current = current.right + } + } + } + } + + findParent(value) { + if (this.root === null) { + return null + } + if (this.root.value === value) { + return this.root + } + let current = this.root + while (current) { + if (current.value > value) { + if (current.left === null) { + return null + } else if (current.left.value === value) { + return current + } else { + current = current.left + } + } else if (current.value < value) { + if (current.right === null) { + return null + } else if (current.right.value === value) { + return current + } else { + current = current.right + } + } + } + } + + remove(value) { + if (this.root === null) { + return + } + + let parent = this.findParent(value) + let toDelete = this.search(value) + // find parent of value + // find node that contains value + // if that node's left child has a right subtree + if (toDelete.left === null && toDelete.right === null) { + if (parent.right.value === toDelete.value) { + parent.right = null + } else { + parent.left = null + } + } else if (toDelete.left.right !== null) { + //traverse to rightmost leaf + let current = toDelete.left.right + while (current.right != null) { + current = current.right + } + //swap values of node and leaf + parent.value = current.value + parent.right = null + // if that node's left child has no right subtree, + } else if (toDelete.left.right === null) { + // then node's right subtree becomes left child's right subtree + toDelete.left.right = toDelete.right + // parent points to left child + parent.left = toDelete.left + } else if (!toDelete.left){ + parent.right = toDelete.right.right + } + + this.size-- + } + + traverse(func, node=this.root) { + if(node !== null) { + this.traverse(func, node.left); + func(node.value) + console.log(node.value); + this.traverse(func, node.right) + } + } + + count() { + return this.size + } +} diff --git a/src/hashTable.js b/src/hashTable.js new file mode 100644 index 0000000..ff306de --- /dev/null +++ b/src/hashTable.js @@ -0,0 +1,66 @@ +'use strict' + +class Node { + constructor(key, value) { + this.key = key; + this.value = value + this.next = null; + } +} + +export default class HashTable { + constructor() { + this.list = new Array(137); + this.count = 0 + } + + hash(key) { + let prime = 37 + let total = 0 + for (var i = 0; i < key.length; i++) { + total += prime * total + key.charCodeAt(i) + } + total = total % this.list.length + return total + } + + put(key, value) { + let hashedKey = this.hash(key) + this.list[hashedKey] = new Node(key, value) + this.count++ + } + + get(key) { + let hashedKey = this.hash(key) + if(!this.list[hashedKey]) { + return null + } + return this.list[hashedKey].value + } + + contains(key) { + let hashedKey = this.hash(key) + if (!this.list[hashedKey]) { + return false + } + return this.list[hashedKey].key === key ? true : false + } + + iterate(func) { + this.list.forEach((elem, index) => { + if (elem) { + func(elem.key, elem.value) + } + }) + } + + remove(key) { + let hashedKey = this.hash(key) + delete this.list[hashedKey] + this.count-- + } + + size() { + return this.count + } +}