diff --git a/Codes/AVLTree.js b/Codes/AVLTree.js new file mode 100644 index 0000000..dca3d03 --- /dev/null +++ b/Codes/AVLTree.js @@ -0,0 +1,203 @@ +// Create node +const Node = function(item){ + this.item = item; + this.height = 1; + this.left = null; + this.right = null; + } + + //AVL Tree + const AVLTree = function(){ + let root = null; + + //return height of the node + this.height = (N) => { + if (N === null){ + return 0; + } + + return N.height; + } + + //right rotate + this.rightRotate = (y) => { + let x = y.left; + let T2 = x.right; + x.right = y; + y.left = T2; + y.height = Math.max(this.height(y.left), this.height(y.right)) + 1; + x.height = Math.max(this.height(x.left), this.height(x.right)) + 1; + return x; + } + + //left rotate + this.leftRotate = (x) => { + let y = x.right; + let T2 = y.left; + y.left = x; + x.right = T2; + x.height = Math.max(this.height(x.left), this.height(x.right)) + 1; + y.height = Math.max(this.height(y.left), this.height(y.right)) + 1; + return y; + } + + // get balance factor of a node + this.getBalanceFactor = (N) => { + if (N == null){ + return 0; + } + + return this.height(N.left) - this.height(N.right); + } + + + // helper function to insert a node + const insertNodeHelper = (node, item) => { + + // find the position and insert the node + if (node === null){ + return (new Node(item)); + } + + if (item < node.item){ + node.left = insertNodeHelper(node.left, item); + }else if (item > node.item){ + node.right = insertNodeHelper(node.right, item); + }else{ + return node; + } + + // update the balance factor of each node + // and, balance the tree + node.height = 1 + Math.max(this.height(node.left), this.height(node.right)); + + let balanceFactor = this.getBalanceFactor(node); + + if (balanceFactor > 1) { + if (item < node.left.item) { + return this.rightRotate(node); + } else if (item > node.left.item) { + node.left = this.leftRotate(node.left); + return this.rightRotate(node); + } + } + + if (balanceFactor < -1) { + if (item > node.right.item) { + return this.leftRotate(node); + } else if (item < node.right.item) { + node.right = this.rightRotate(node.right); + return this.leftRotate(node); + } + } + + return node; + } + + // insert a node + this.insertNode = (item) => { + // console.log(root); + root = insertNodeHelper(root, item); + } + + //get node with minimum value + this.nodeWithMimumValue = (node) => { + let current = node; + while (current.left !== null){ + current = current.left; + } + return current; + } + + // delete helper + const deleteNodeHelper = (root, item) => { + + // find the node to be deleted and remove it + if (root == null){ + return root; + } + if (item < root.item){ + root.left = deleteNodeHelper(root.left, item); + }else if (item > root.item){ + root.right = deleteNodeHelper(root.right, item); + }else { + if ((root.left === null) || (root.right === null)) { + let temp = null; + if (temp == root.left){ + temp = root.right; + }else{ + temp = root.left; + } + + if (temp == null) { + temp = root; + root = null; + } else{ + root = temp; + } + } else { + let temp = this.nodeWithMimumValue(root.right); + root.item = temp.item; + root.right = deleteNodeHelper(root.right, temp.item); + } + } + if (root == null){ + return root; + } + + // Update the balance factor of each node and balance the tree + root.height = Math.max(this.height(root.left), this.height(root.right)) + 1; + + let balanceFactor = this.getBalanceFactor(root); + if (balanceFactor > 1) { + if (this.getBalanceFactor(root.left) >= 0) { + return this.rightRotate(root); + } else { + root.left = this.leftRotate(root.left); + return this.rightRotate(root); + } + } + if (balanceFactor < -1) { + if (this.getBalanceFactor(root.right) <= 0) { + return this.leftRotate(root); + } else { + root.right = this.rightRotate(root.right); + return this.leftRotate(root); + } + } + return root; + } + + //delete a node + this.deleteNode = (item) => { + root = deleteNodeHelper(root, item); + } + + // print the tree in pre - order + this.preOrder = () => { + preOrderHelper(root); + } + + const preOrderHelper = (node) => { + if (node) { + console.log(node.item); + preOrderHelper(node.left); + preOrderHelper(node.right); + } + } + } + +// Insert: +var tree = new AVLTree(); +tree.insertNode(33); +tree.insertNode(13); +tree.insertNode(53); +tree.insertNode(9); +tree.insertNode(21); +tree.insertNode(61); +tree.insertNode(8); +tree.insertNode(11); +tree.preOrder(); +tree.deleteNode(13); +console.log("After Deletion: "); +tree.preOrder(); \ No newline at end of file diff --git a/Codes/BinarySearchTree.js b/Codes/BinarySearchTree.js new file mode 100644 index 0000000..4527faf --- /dev/null +++ b/Codes/BinarySearchTree.js @@ -0,0 +1,318 @@ +// Node class +class Node +{ + constructor(data) + { + this.data = data; + this.left = null; + this.right = null; + } +} + + +// Binary Search tree class +class BinarySearchTree +{ + constructor() + { + // root of a binary search tree + this.root = null; + } + + // function to be implemented + // insert(data) + // remove(data) + + + // Helper function + // findMinNode() + // getRootNode() + // inorder(node) + // preorder(node) + // postorder(node) + // search(node, data) + + // helper method which creates a new node to +// be inserted and calls insertNode +insert(data){ + // Creating a node and initailising + // with data + var newNode = new Node(data); + + // root is null then node will + // be added to the tree and made root. + if(this.root === null) + this.root = newNode; + else + + // find the correct position in the + // tree and add the node + this.insertNode(this.root, newNode); +} + +// Method to insert a node in a tree +// it moves over the tree to find the location +// to insert a node with a given data +insertNode(node, newNode) +{ + // if the data is less than the node + // data move left of the tree + if(newNode.data < node.data) + { + // if left is null insert node here + if(node.left === null) + node.left = newNode; + else + + // if left is not null recur until + // null is found + this.insertNode(node.left, newNode); + } + + // if the data is more than the node + // data move right of the tree + else + { + // if right is null insert node here + if(node.right === null) + node.right = newNode; + else + + // if right is not null recur until + // null is found + this.insertNode(node.right,newNode); + } +} + +// helper method that calls the +// removeNode with a given data +remove(data) +{ + // root is re-initialized with + // root of a modified tree. + this.root = this.removeNode(this.root, data); +} + +// Method to remove node with a +// given data +// it recur over the tree to find the +// data and removes it +removeNode(node, key) +{ + + // if the root is null then tree is + // empty + if(node === null) + return null; + + // if data to be delete is less than + // roots data then move to left subtree + else if(key < node.data) + { + node.left = this.removeNode(node.left, key); + return node; + } + + // if data to be delete is greater than + // roots data then move to right subtree + else if(key > node.data) + { + node.right = this.removeNode(node.right, key); + return node; + } + + // if data is similar to the root's data + // then delete this node + else + { + // deleting node with no children + if(node.left === null && node.right === null) + { + node = null; + return node; + } + + // deleting node with one children + if(node.left === null) + { + node = node.right; + return node; + } + + else if(node.right === null) + { + node = node.left; + return node; + } + + // Deleting node with two children + // minumum node of the rigt subtree + // is stored in aux + var aux = this.findMinNode(node.right); + node.data = aux.data; + + node.right = this.removeNode(node.right, aux.data); + return node; + } + +} + +// Performs inorder traversal of a tree +inorder(node) +{ + if(node !== null) + { + this.inorder(node.left); + console.log(node.data); + this.inorder(node.right); + } +} + +// Performs preorder traversal of a tree +preorder(node) +{ + if(node !== null) + { + console.log(node.data); + this.preorder(node.left); + this.preorder(node.right); + } +} + +// Performs postorder traversal of a tree +postorder(node) +{ + if(node !== null) + { + this.postorder(node.left); + this.postorder(node.right); + console.log(node.data); + } +} + +// finds the minimum node in tree +// searching starts from given node +findMinNode(node) +{ + // if left of a node is null + // then it must be minimum node + if(node.left === null) + return node; + else + return this.findMinNode(node.left); +} + +// returns root of the tree +getRootNode() +{ + return this.root; +} + +// search for a node with given data +search(node, data) +{ +// if trees is empty return null + if(node === null) + return null; + + // if data is less than node's data + // move left + else if(data < node.data) + return this.search(node.left, data); + + // if data is less than node's data + // move left + else if(data > node.data) + return this.search(node.right, data); + + // if data is equal to the node data + // return node + else + return node; +} +} + + + +// create an object for the BinarySearchTree +var BST = new BinarySearchTree(); + +// Inserting nodes to the BinarySearchTree +BST.insert(15); +BST.insert(25); +BST.insert(10); +BST.insert(7); +BST.insert(22); +BST.insert(17); +BST.insert(13); +BST.insert(5); +BST.insert(9); +BST.insert(27); + +// 15 +// / \ +// 10 25 +// / \ / \ +// 7 13 22 27 +// / \ / +// 5 9 17 + +var root = BST.getRootNode(); + +// prints 5 7 9 10 13 15 17 22 25 27 +BST.inorder(root); + +// Removing node with no children +BST.remove(5); + + +// 15 +// / \ +// 10 25 +// / \ / \ +// 7 13 22 27 +// \ / +// 9 17 + + +var root = BST.getRootNode(); + +// prints 7 9 10 13 15 17 22 25 27 +BST.inorder(root); + +// Removing node with one child +BST.remove(7); + +// 15 +// / \ +// 10 25 +// / \ / \ +// 9 13 22 27 +// / +// 17 + + +var root = BST.getRootNode(); + +// prints 9 10 13 15 17 22 25 27 +BST.inorder(root); + +// Removing node with two children +BST.remove(15); + +// 17 +// / \ +// 10 25 +// / \ / \ +// 9 13 22 27 + +var root = BST.getRootNode(); +console.log("inorder traversal"); + +// prints 9 10 13 17 22 25 27 +BST.inorder(root); + +console.log("postorder traversal"); +BST.postorder(root); +console.log("preorder traversal"); +BST.preorder(root); diff --git a/Codes/BinaryTree.js b/Codes/BinaryTree.js new file mode 100644 index 0000000..4cf5966 --- /dev/null +++ b/Codes/BinaryTree.js @@ -0,0 +1,20 @@ +class TreeNode { + constructor(value) { + this.value = value; + this.descendants = []; + } + } + + // create nodes with values +const abe = new TreeNode('Abe'); +const homer = new TreeNode('Homer'); +const bart = new TreeNode('Bart'); +const lisa = new TreeNode('Lisa'); +const maggie = new TreeNode('Maggie'); + +// associate root with is descendants +abe.descendants.push(homer); +homer.descendants.push(bart, lisa, maggie); + +console.log(abe.descendants); // ['Homer', 'Bart', 'Lisa', 'Maggie'] +console.log(homer.descendants); // ['Bart', 'Lisa', 'Maggie'] \ No newline at end of file diff --git a/Codes/DoublyLinkedList.js b/Codes/DoublyLinkedList.js new file mode 100644 index 0000000..e19610b --- /dev/null +++ b/Codes/DoublyLinkedList.js @@ -0,0 +1,172 @@ +//DOUBLY LINKED +class Node { + constructor(value) { + this.value = value; + this.prev = null; + this.next = null; + } + } + class DoublyLinkedList { + constructor() { + this.head = null; + this.tail = null; + this.length = 0; + } + + push(val) { + const newNode = new Node(val); + if (!this.head) { + this.head = newNode; + this.tail = newNode; + } else { + this.tail.next = newNode; + newNode.prev = this.tail; + this.tail = newNode; + } + this.length++; + return this; + } + + pop() { + //in case of empty list + if (this.length === 0) { + return false; + } + //get popped node + const popped = this.tail; + //save newTail to a variable (could be null) + const newTail = this.tail.prev; + //if newTail is not null + if (newTail) { + //sever connection to popped node + newTail.next = null; + //sever connection from popped node + this.tail.prev = null; + //in case of 1 length list + } else { + //make sure to edit head in case newTail is null + this.head = null; + } + //assign new tail (could be null) + this.tail = newTail; + // subtract length + this.length--; + + return popped; + } + + shift() { + //in case list is empty + if (!this.head) { + return false; + } + //save shifted node to variable + const shiftedNode = this.head; + //make the new head the next (might be null) + const newHead = this.head.next; //might be null + //if list is more than 1 + if (this.head !== this.tail) { + newHead.prev = null; + shiftedNode.next = null; + } else { + this.tail = null; + } + this.head = newHead; + this.length--; + return shiftedNode; + } + + unshift(val) { + const newNode = new Node(val); + if (!this.head) { + this.head = newNode; + this.tail = newNode; + } else { + this.head.prev = newNode; + newNode.next = this.head; + this.head = newNode; + } + this.length++; + return this; + } + + insertAtIndex(index, val) { + //if index doesn't exist + if (index > this.length) { + return false; + } + if (index === 0) { + this.unshift(val); + } else if (index === this.length) { + this.push(val); + } else { + const newNode = new Node(val); + const after = this.accessAtIndex(index); + const before = after.prev; + after.prev = newNode; + before.next = newNode; + newNode.next = after; + newNode.prev = before; + this.length++; + } + return this; + } + + removeAtIndex(index) { + let removedNode; + if (index >= this.length) { + return false; + } + if (index == 0) { + removedNode = this.shift(); + } else if (index == this.length - 1) { + removedNode = this.pop(); + } else { + removedNode = this.getNodeAtIndex(index); + const after = removedNode.next; + const before = removedNode.prev; + removedNode.next = null; + removedNode.prev = null; + before.next = after; + after.prev = before; + this.length--; + } + return removedNode; + } + + getNodeAtIndex(index) { + if (index >= this.length || index < 0) { + return false; + } + let currentIndex = 0; + let currentNode = this.head; + while (currentIndex !== index) { + currentNode = currentNode.next; + currentIndex++; + } + return currentNode; + } + + setNodeAtIndex(index, val) { + const foundNode = this.getNodeAtIndex(index) + if(foundNode){ + foundNode.value = val + return foundNode; + } + return null; + } + + printList() { + console.log(list) + if(this.head){ + let current = this.head; + while (current.next) { + console.log(current); + current = current.next; + } + console.log(current); + } else { + console.log("empty list") + } + } + } \ No newline at end of file diff --git a/Codes/HashTable.js b/Codes/HashTable.js new file mode 100644 index 0000000..6b86696 --- /dev/null +++ b/Codes/HashTable.js @@ -0,0 +1,41 @@ +class HashTable { + constructor() { + this.values = {}; + this.length = 0; + this.size = 0; + } + + calculateHash(key) { + return key.toString().length % this.size; + } + + add(key, value) { + const hash = this.calculateHash(key); + if (!this.values.hasOwnProperty(hash)) { + this.values[hash] = {}; + } + if (!this.values[hash].hasOwnProperty(key)) { + this.length++; + } + this.values[hash][key] = value; + } + + search(key) { + const hash = this.calculateHash(key); + if (this.values.hasOwnProperty(hash) && this.values[hash].hasOwnProperty(key)) { + return this.values[hash][key]; + } else { + return null; + } + } + } + + //create object of type hash table + const ht = new HashTable(); + //add data to the hash table ht + ht.add("Canada", "300"); + ht.add("Germany", "100"); + ht.add("Italy", "50"); + + //search + console.log(ht.search("Italy")); \ No newline at end of file diff --git a/Codes/HashTableBucketChaining.js b/Codes/HashTableBucketChaining.js new file mode 100644 index 0000000..0302ec2 --- /dev/null +++ b/Codes/HashTableBucketChaining.js @@ -0,0 +1,101 @@ +// Hash Table with bucket chaining + + +// Array of buckets +var buckets = new Array(); + +// Initialize the hash table + +function init() { + for (var i = 0; i < 10; i++) { + buckets[i] = new Array(); + } +} + +// Insert a new key-value pair into the hash table + +function insert(key, value) { + var hash = Math.abs(key % 10); + buckets[hash].push([key, value]); + console.log(buckets); +} + +// Search for a key in the hash table + +function search(key) { + var hash = Math.abs(key % 10); + for (var i = 0; i < buckets[hash].length; i++) { + if (buckets[hash][i][0] == key) { + return buckets[hash][i][1]; + } + } + return null; +} + +// Delete a key-value pair from the hash table + +function deleteKey(key) { + var hash = Math.abs(key % 10); + for (var i = 0; i < buckets[hash].length; i++) { + if (buckets[hash][i][0] == key) { + buckets[hash].splice(i, 1); + return; + } + } +} + +// Delete all key-value pairs from the hash table + +function deleteAll() { + for (var i = 0; i < buckets.length; i++) { + buckets[i] = new Array(); + } +} + +// Test the hash table + +(function test() { + init(); + insert(1, "one"); + insert(2, "two"); + insert(3, "three"); + insert(4, "four"); + insert(5, "five"); + insert(6, "six"); + insert(7, "seven"); + insert(8, "eight"); + insert(9, "nine"); + insert(10, "ten"); + insert(11, "eleven"); + insert(12, "twelve"); + insert(13, "thirteen"); + insert(14, "fourteen"); + insert(15, "fifteen"); + insert(16, "sixteen"); + insert(17, "seventeen"); + insert(18, "eighteen"); + insert(19, "nineteen"); + insert(20, "twenty"); + insert(30, "thirty"); + insert(40, "forty"); + insert(50, "fifty"); + insert(60, "sixty"); + insert(70, "seventy"); + insert(80, "eighty"); + insert(90, "ninety"); + insert(100, "hundred"); + insert(1000, "thousand"); + insert(1000000, "million"); + insert(1000000000, "billion"); + insert(1000000000000, "trillion"); + insert(1000000000000000, "quadrillion"); + insert(1000000000000000000, "quintillion"); + insert(1000000000000000000000, "sextillion"); + insert(1000000000000000000000000, "septillion"); + insert(1000000000000000000000000000, "octillion"); + insert(1000000000000000000000000000000, "nonillion"); + insert(1000000000000000000000000000000000, "decillion"); + insert(1000000000000000000000000000000000000, "undecillion"); + insert(100000000000000000000000000000000000000000, "duodecillion"); + insert(100000000000000000000000000000000000000000000, "tredecillion"); +})(); \ No newline at end of file diff --git a/Codes/PriorityQueue.js b/Codes/PriorityQueue.js new file mode 100644 index 0000000..85539e4 --- /dev/null +++ b/Codes/PriorityQueue.js @@ -0,0 +1,87 @@ +const top = 0; +const parent = i => ((i + 1) >>> 1) - 1; +const left = i => (i << 1) + 1; +const right = i => (i + 1) << 1; + +class PriorityQueue { + constructor(comparator = (a, b) => a > b) { + this._heap = []; + this._comparator = comparator; + } + size() { + return this._heap.length; + } + isEmpty() { + return this.size() == 0; + } + peek() { + return this._heap[top]; + } + push(...values) { + values.forEach(value => { + this._heap.push(value); + this._siftUp(); + }); + return this.size(); + } + pop() { + const poppedValue = this.peek(); + const bottom = this.size() - 1; + if (bottom > top) { + this._swap(top, bottom); + } + this._heap.pop(); + this._siftDown(); + return poppedValue; + } + replace(value) { + const replacedValue = this.peek(); + this._heap[top] = value; + this._siftDown(); + return replacedValue; + } + _greater(i, j) { + return this._comparator(this._heap[i], this._heap[j]); + } + _swap(i, j) { + [this._heap[i], this._heap[j]] = [this._heap[j], this._heap[i]]; + } + _siftUp() { + let node = this.size() - 1; + while (node > top && this._greater(node, parent(node))) { + this._swap(node, parent(node)); + node = parent(node); + } + } + _siftDown() { + let node = top; + while ( + (left(node) < this.size() && this._greater(left(node), node)) || + (right(node) < this.size() && this._greater(right(node), node)) + ) { + let maxChild = (right(node) < this.size() && this._greater(right(node), left(node))) ? right(node) : left(node); + this._swap(node, maxChild); + node = maxChild; + } + } +} + +{const top=0,parent=c=>(c+1>>>1)-1,left=c=>(c<<1)+1,right=c=>c+1<<1;class PriorityQueue{constructor(c=(d,e)=>d>e){this._heap=[],this._comparator=c}size(){return this._heap.length}isEmpty(){return 0==this.size()}peek(){return this._heap[top]}push(...c){return c.forEach(d=>{this._heap.push(d),this._siftUp()}),this.size()}pop(){const c=this.peek(),d=this.size()-1;return d>top&&this._swap(top,d),this._heap.pop(),this._siftDown(),c}replace(c){const d=this.peek();return this._heap[top]=c,this._siftDown(),d}_greater(c,d){return this._comparator(this._heap[c],this._heap[d])}_swap(c,d){[this._heap[c],this._heap[d]]=[this._heap[d],this._heap[c]]}_siftUp(){for(let c=this.size()-1;c>top&&this._greater(c,parent(c));)this._swap(c,parent(c)),c=parent(c)}_siftDown(){for(let d,c=top;left(c) 50 +console.log('Size:', queue.size()); //=> 5 +console.log('Contents:'); +while (!queue.isEmpty()) { + console.log(queue.pop()); //=> 40, 30, 20, 10 +} + +// Pairwise comparison semantics +const pairwiseQueue = new PriorityQueue((a, b) => a[1] > b[1]); +pairwiseQueue.push(['low', 0], ['medium', 5], ['high', 10]); +console.log('\nContents:'); +while (!pairwiseQueue.isEmpty()) { + console.log(pairwiseQueue.pop()[0]); //=> 'high', 'medium', 'low' +} \ No newline at end of file diff --git a/Codes/Queue.js b/Codes/Queue.js new file mode 100644 index 0000000..e2b932f --- /dev/null +++ b/Codes/Queue.js @@ -0,0 +1,40 @@ +var Queue = function() { + this.first = null; + this.size = 0; + }; + + var Node = function(data) { + this.data = data; + this.next = null; + }; + + Queue.prototype.enqueue = function(data) { + var node = new Node(data); + + if (!this.first){ + this.first = node; + } else { + n = this.first; + while (n.next) { + n = n.next; + } + n.next = node; + } + + this.size += 1; + return node; + }; + + Queue.prototype.dequeue = function() { + temp = this.first; + this.first = this.first.next; + this.size -= 1; + return temp; + }; + + + var queue = []; + queue.push(2); // queue is now [2] + queue.push(5); // queue is now [2, 5] + var i = queue.shift(); // queue is now [5] + console.log(i); \ No newline at end of file diff --git a/Codes/Stack.js b/Codes/Stack.js new file mode 100644 index 0000000..b3dcb08 --- /dev/null +++ b/Codes/Stack.js @@ -0,0 +1,91 @@ + +class Stack { + constructor(){ + this.data = []; + this.top = 0; + } + push(element) { + this.data[this.top] = element; + this.top = this.top + 1; + } + length() { + return this.top; + } + peek() { + return this.data[this.top-1]; + } + isEmpty() { + return this.top === 0; + } + pop() { + if( this.isEmpty() === false ) { + this.top = this.top -1; + return this.data.pop(); // removes the last element + } + } + print() { + var top = this.top - 1; // because top points to index where new element to be inserted + while(top >= 0) { // print upto 0th index + console.log(this.data[top]); + top--; + } + } + reverse() { + this._reverse(this.top - 1 ); + } + _reverse(index) { + if(index != 0) { + this._reverse(index-1); + } + console.log(this.data[index]); + } +} + +let stack = []; + +stack.push(1); +console.log(stack); // [1] + +stack.push(2); +console.log(stack); // [1,2] + +stack.push(3); +console.log(stack); // [1,2,3] + +stack.push(4); +console.log(stack); // [1,2,3,4] + +stack.push(5); +console.log(stack); // [1,2,3,4,5] + +console.log(stack.pop()); // 5 +console.log(stack); // [1,2,3,4]; + +console.log(stack.pop()); // 4 +console.log(stack); // [1,2,3]; + +console.log(stack.pop()); // 3 +console.log(stack); // [1,2]; + +console.log(stack.pop()); // 2 +console.log(stack); // [1]; + +console.log(stack.pop()); // 1 +console.log(stack); // []; -> empty + +console.log(stack.pop()); // undefined + +function reverse(str) { + let stack = []; + // push letter into stack + for (let i = 0; i < str.length; i++) { + stack.push(str[i]); + } + // pop letter from the stack + let reverseStr = ''; + while (stack.length > 0) { + reverseStr += stack.pop(); + } + return reverseStr; +} +console.log(reverse('JavaScript Stack')); //kcatS tpircSavaJ \ No newline at end of file diff --git a/Codes/singlyLinkedList.js b/Codes/singlyLinkedList.js new file mode 100644 index 0000000..a4604e3 --- /dev/null +++ b/Codes/singlyLinkedList.js @@ -0,0 +1,192 @@ +class Node { + constructor(value) { + this.value = value; + this.next = null; + } +} + +// linkedlist class +class SinglyLinkedList { + constructor() { + this.head = null; + this.tail = null; + this.length = 0; + } +} + +// adds a node to the end of the list +push = (val) => { + //make a new Node + const newNode = new Node(val); + //if list is empty, make node head and tail + if(!this.head){ + this.head = newNode; + this.tail = newNode; + //otherwise add node to the end of the list + } else { + this.tail.next = newNode; + this.tail = newNode; + } + //add 1 to the length + this.length++; + //return list + return this; +} + +unshift = (val) => { + const newNode = new Node(val); + //if list is empty, make node head and tail + if(!this.head){ + this.head = newNode; + this.tail = newNode; + //add current head pointer to new head(new node), and make new node new head + }else{ + newNode.next = this.head; + this.head = newNode; + } + //add 1 to the length + this.length++; + //return list + return this; +} + +// removes first item in list +shift = () => { + //if list is empty, return false + if(!this.head){ + return false; + } + //get shifted node + const shiftNode = this.head; + //get new head (could be NULL if list is length 2) + const newHead = this.head.next; + //if newHead is null, reassign tail to newHead(null) + if(!newHead) { + this.tail = newHead; + } + //assign new head + this.head = newHead; + //remove 1 from length + this.length --; + //return shifted node + return shiftNode; +} + +//return node at given index +getNodeAtIndex = (index) => { + //if index is not within list return null + if(index >= this.length || index < 0){ + return null; + } + //iterate through list until finding the one at index + let currentNode = this.head; + let currentIndex = 0; + while(currentIndex !== index){ + currentNode = currentNode.next; + currentIndex++; + } + return currentNode; +} + + + +// removes a node from the end of the list +pop = () => { + //if list is empty(has no tail) return false + if(!this.tail){ + return false; + } + //get removed node + const poppedNode = this.tail; + // if list has more than 1 node + if(this.head !== this.tail){ + //find new tail + const newTail = this.getNodeAtIndex(this.length - 2); + //remove newtail's reference to poppedNode + newTail.next = null; + //make it new tail + this.tail = newTail; + //otherwise establish list is empty + } else { + this.head = null; + this.tail = null; + } + //subtract 1 from length + this.length--; + //return poppedNode + return poppedNode; +} + +//change the node at the given index +setNodeAtIndex = (val, index) => { + //find the node using already built method + const foundNode = this.getNodeAtIndex(index); + //if the node is found update and return + if(foundNode){ + foundNode.value = val; + return foundNode; + } + //else return null + return null; +} + +//insert a new node at the index with the given value +insertAtIndex = (index, val) => { + //if index not valid return false + if (index < 0 || index > this.length) { + return false; + } + //if index is zero use already built unshift + if (index === 0) { + return this.unshift(val); + } + //if index is at end of list, use already built push + if (index === this.length) { + return this.push(val); + } + // make a new node, find the nodes before and after it, make before's next the new node, and the newNode's next the after + const newNode = new Node(val); + const after = this.getNodeAtIndex(index); + const before = this.getNodeAtIndex(index - 1); + newNode.next = after; + before.next = newNode; + + this.length++; + return this; +} + +// remove node from index +removeFrom = (index) => { + //if index is invalid return false + if (index < 0 || index >= this.length) { + return false; + } + //if index is at beginning use already built shift + if (index === 0) return this.shift(); + //if index is at end use already built pop + if (index === this.length - 1) return this.pop(); + //get before and after, set before's next to after and remove after's reference from removed Node + const before = this.getNodeAtIndex(index - 1); + const removedNode = this.getNodeAtIndex(index); + before.next = removedNode.next; + removedNode.next = null; + + this.length--; + return removedNode; + } + +printList = () => { + console.log(list) + if(this.head){ + let current = this.head; + while (current.next) { + console.log(current); + current = current.next; + } + console.log(current); + } else { + console.log("empty list") + } + } + +