commit 57cfa8ad1999fb198dd91734b383a568756a3f0d Author: Timothy Warren Date: Fri Jul 26 16:25:13 2019 -0400 First commit diff --git a/package.json b/package.json new file mode 100644 index 0000000..6c56408 --- /dev/null +++ b/package.json @@ -0,0 +1,4 @@ +{ + "type": "module", + "main": "./src/usage.js" +} \ No newline at end of file diff --git a/src/structures/binary-search-tree.js b/src/structures/binary-search-tree.js new file mode 100644 index 0000000..dc16e87 --- /dev/null +++ b/src/structures/binary-search-tree.js @@ -0,0 +1,118 @@ +const root = Symbol("root"); + +class BinarySearchTreeNode { + constructor (value) { + this.value = value; + this.left = null; + this.right = null; + } +} + +class BinarySearchTree { + constructor () { + this[root] = null; + } + + add (value) { + const node = new BinarySearchTreeNode(value); + let current = null; + + // No items in tree + if (this[root] === null) { + this[root] = node; + } else { + current = this[root]; + + while (true) { + if (value < current.value) { + // If there's no left, insert + if (current.left === null) { + current.left = node; + break; + } + + current = current.left; + + } else if (value > current.value) { + if (current.right === null) { + current.right = node; + break; + } + + current = current.right; + } else { + break; + } + } + } + } + + contains (value) { + let found = false; + let current = this[root]; + + while ( ! found && current !== null) { + if (value < current.value) { + current = current.left; + } else if (value > current.value) { + current = current.right; + } else if (value === current.value) { + found = true; + } + } + + return found; + } + + remove (value) { + + } + + size () { + let length = 0; + + this.traverse(() => { + length++; + }); + + return length; + } + + traverse (process) { + const inOrder = (node) => { + if ( ! node) { + return; + } + + // traverse the left subtree + if (node.left !== null) { + inOrder(node.left); + } + + // handle the current node + process.call(this, node); + + // traverse the right subtree + if (node.right !== null) { + inOrder(node.right); + } + }; + + // start with the root + inOrder(this[root]); + } + + toArray () { + let result = []; + + this.traverse((node) => { + result.push(node.value); + }); + + return result; + } + + toString () { + return this.toArray().toString(); + } +} \ No newline at end of file diff --git a/src/structures/circular-doubly-linked-list.js b/src/structures/circular-doubly-linked-list.js new file mode 100644 index 0000000..ee9c369 --- /dev/null +++ b/src/structures/circular-doubly-linked-list.js @@ -0,0 +1,131 @@ +const head = Symbol("head"); +const tail = Symbol("tail"); + +class CircularDoublyLinkedListNode { + constructor (data) { + this.data = data; + this.next = null; + this.previous = null; + } +} + +export class CircularDoublyLinkedList { + constructor () { + this[head] = null; + this[tail] = null; + } + + add (data) { + const newNode = new CircularDoublyLinkedListNode(data); + + // Special case: no items in the list + if (this[head] === null) { + this[head] = newNode; + newNode.next = newNode; + newNode.previous = newNode; + } else { + const tail = this[head].previous; + + tail.next = newNode; + newNode.previous = tail; + newNode.next = this[head]; + this[head].previous = newNode; + } + } + + get (index) { + if ( ! Number(index) < 0) { + return undefined; + } + + let current = this[head]; + let i = 0; + + do { + if (i === index) { + return current.data; + } + + current = current.next; + i++; + } while ((current !== this[head]) && (i <= index)); + + return undefined; + } + + remove (index) { + if ((this[head] === null) || (index < 0)) { + throw new RangeError(`Index ${index} does not exist in the list.`); + } + + let current = this[head]; + + // Removing the first node + if (index === 0) { + if (current.next === this[head]) { + this[head] = null; + } else { + const tail = this[head].previous; + + // Set the tail to point to the second item in the list + // and point the new item to the tail + tail.next = current.next; + current.next.previous = tail; + + this[head] = tail.next; + } + + return current.data; + } + + let i = 0; + + do { + current = current.next; + i++; + } while ((current !== this[head]) && (i < index)); + + if (current !== this[head]) { + // skip over the node to remove it + current.previous.next = current.next; + current.next.previous = current.previous; + + return current.data; + } + + throw new RangeError(`Index ${index} does not exist in the list.`); + } + + *values () { + // list is empty + if (this[head] !== null) { + // only one node + if (this[head].next === this[head]) { + yield this[head].data; + } else { + let current = this[head]; + + do { + yield current.data; + current = current.next; + } while (current !== this[head]); + } + } + } + + *circularValues () { + if (this[head] !== null) { + let current = this[head]; + + // infinite loop + while (true) { + yield current.data; + current = current.next; + } + } + } + + [Symbol.iterator] () { + return this.values(); + } +} \ No newline at end of file diff --git a/src/structures/doubly-linked-list.js b/src/structures/doubly-linked-list.js new file mode 100644 index 0000000..2c9aa7c --- /dev/null +++ b/src/structures/doubly-linked-list.js @@ -0,0 +1,117 @@ +const head = Symbol("head"); +const tail = Symbol("tail"); + +class DoublyLinkedListNode { + constructor (data) { + this.data = data; + this.next = null; + this.previous = null; + } +} + +export class DoublyLinkedList { + constructor () { + this[head] = null; + this[tail] = null; + } + + add (data) { + const newNode = new DoublyLinkedListNode(data); + + // special case: no nodes + if (this[head] === null) { + this[head] = newNode; + } else { + // link the current tail and new tail + this[tail].next = newNode; + newNode.previous = this[tail]; + } + + // re-assign the tail + this[tail] = newNode; + } + + get (index) { + if ( ! Number(index) < 0) { + return undefined; + } + + let current = this[head]; + let i = 0; + + while ((current !== null) && (i < index)) { + current = current.next; + i++; + } + + return current !== null + ? current.data + : undefined; + } + + remove (index) { + if ((this[head] === null) || (index < 0)) { + throw new RangeError(`Index ${index} does not exist in the list.`); + } + + // special case: removing the first node + if (index === 0) { + const data = this[head].data; + this[head] = this[head].next; + + // Only one node + if (this[head] === null) { + this[tail] = null; + } else { + this[head].previous = null; + } + + return data; + } + + let current = this[head]; + let i = 0; + while ((current !== null) && (i < index)) { + current = current.next; + i++; + } + + // If node was found remove it + if (current !== null) { + current.previous.next = current.next; + + // last node + if (this[tail] === current) { + this[tail] = current.previous; + } else { + current.next.previous = current.previous; + } + + return current.data; + } + + throw new RangeError(`Index ${index} does not exist in the list.`); + } + + *values () { + let current = this[head]; + + while (current !== null) { + yield current.data; + current = current.next; + } + } + + *reverse () { + let current = this[tail]; + + while (current !== null) { + yield current.data; + current = current.previous; + } + } + + [Symbol.iterator] () { + return this.values(); + } +} \ No newline at end of file diff --git a/src/structures/index.js b/src/structures/index.js new file mode 100644 index 0000000..21b3916 --- /dev/null +++ b/src/structures/index.js @@ -0,0 +1,9 @@ +import { CircularDoublyLinkedList } from "./circular-doubly-linked-list"; +import { DoublyLinkedList } from "./doubly-linked-list"; +import { LinkedList } from "./linked-list"; + +export { + CircularDoublyLinkedList, + DoublyLinkedList, + LinkedList, +}; \ No newline at end of file diff --git a/src/structures/linked-list.js b/src/structures/linked-list.js new file mode 100644 index 0000000..5d8098e --- /dev/null +++ b/src/structures/linked-list.js @@ -0,0 +1,103 @@ +const head = Symbol("head"); + +class LinkedListNode { + constructor (data) { + this.data = data; + this.next = null; + } +} + +export class LinkedList { + constructor () { + this[head] = null; + } + + add (data) { + const newNode = new LinkedListNode(data); + + if (this[head] === null) { + this[head] = newNode; + } else { + let current = this[head]; + + // Go to the end of the list + while (current.next !== null) { + current = current.next; + } + + // On the last node, add the new node + current.next = newNode; + } + } + + get (index) { + if ( ! Number(index) < 0) { + return undefined; + } + + let current = this[head]; + let i = 0; + + while ((current !== null) && (i < index)) { + current = current.next; + i++; + } + + return current !== null + ? current.data + : undefined; + } + + remove (index) { + // Invalid index? + if ((this[head] === null) || (index < 0)) { + throw new RangeError(`Index ${index} does not exist in the list.`); + } + + // Remove the first node + if (index === 0) { + const data = this[head].data; + + // Replace the head with the next node + this[head] = this[head].next; + + return data; + } + + let current = this[head]; + let previous = null; + let i = 0; + + while ((current !== null) && (i < index)) { + // Save the current value + previous = current; + + current = current.next; + + i++; + } + + // Remove the node if found + if (current !== null) { + // skip over the node to "remove" it + previous.next = current.next; + + return current.data; + } + + throw new RangeError(`Index ${index} does not exist in the list.`); + } + + *values () { + let current = this[head]; + + while (current !== null) { + yield current.data; + current = current.next; + } + } + + [Symbol.iterator] () { + return this.values(); + } +} \ No newline at end of file diff --git a/src/usage.js b/src/usage.js new file mode 100644 index 0000000..31c3699 --- /dev/null +++ b/src/usage.js @@ -0,0 +1,83 @@ +import { + CircularDoublyLinkedList, + DoublyLinkedList, + LinkedList, +} from "./structures/index.js"; + +function useLinkedList () { + console.log('-----------------------------------------------------------------------------------------------------'); + console.log('Linked List'); + console.log('-----------------------------------------------------------------------------------------------------'); + const list = new LinkedList(); + list.add("red"); + list.add("orange"); + list.add("yellow"); + + // get the second item in the list + console.log('Index 1 in list', list.get(1)); + + // print out all the items + for (const color of list) { + console.log(color); + } + + // remove the second item in the list + console.log('Removing list item with index 1', list.remove(1)); + + // get the item now with index 1 + console.log('New list item with index 1', list.get(1)); +} + +function useDoublyLinkedList () { + console.log('-----------------------------------------------------------------------------------------------------'); + console.log('Doubly-Linked List'); + console.log('-----------------------------------------------------------------------------------------------------'); + const list = new DoublyLinkedList(); + list.add("red"); + list.add("orange"); + list.add("yellow"); + + console.log('Index 1 in list',list.get(1)); + + for (const color of list.reverse()) { + console.log(color); + } + + // remove the second item in the list + console.log('Removing list item with index 1', list.remove(1)); + + // get the item now with index 1 + console.log('New list item with index 1', list.get(1)); +} + +function useCircularDoublyLinkedList () { + console.log('-----------------------------------------------------------------------------------------------------'); + console.log('Circular Doubly-Linked List'); + console.log('-----------------------------------------------------------------------------------------------------'); + + const list = new CircularDoublyLinkedList(); + list.add("red"); + list.add("orange"); + list.add("yellow"); + + // get the second item in the list + console.log('Index 1 in list', list.get(1)); // "orange" + + for (const color of list.values()) { + console.log(color); + } + + // remove the second item in the list + console.log('Removing list item with index 1', list.remove(1)); + + // get the item now with index 1 + console.log('New list item with index 1', list.get(1)); +} + +(() => { + useLinkedList(); + console.log(''); + useDoublyLinkedList(); + console.log(''); + useCircularDoublyLinkedList(); +})(); \ No newline at end of file