diff --git a/DIRECTORY.md b/DIRECTORY.md index 9fd2d090c3..7113becd7a 100644 --- a/DIRECTORY.md +++ b/DIRECTORY.md @@ -115,6 +115,14 @@ * [LinearSearch](https://github.com/TheAlgorithms/Javascript/blob/master/Search/LinearSearch.js) * [StringSearch](https://github.com/TheAlgorithms/Javascript/blob/master/Search/StringSearch.js) +## Slow and Fast Pointers + * [HasCycle](https://github.com/TheAlgorithms/Javascript/blob/master/Slow-Fast-Pointer/HasLinkedListCycle.js) + * [CycleLength](https://github.com/TheAlgorithms/Javascript/blob/master/Slow-Fast-Pointer/LinkedListCycleLength.js) + * [Middle](https://github.com/TheAlgorithms/Javascript/blob/master/Slow-Fast-Pointer/MiddleOfLinkedList.js) + * [ReArrange](https://github.com/TheAlgorithms/Javascript/blob/master/Slow-Fast-Pointer/ReArrangeLinkedList.js) + * [StartOfCycle](https://github.com/TheAlgorithms/Javascript/blob/master/Slow-Fast-Pointer/StartOfLinkedListCycle.js) + * [IsPalindrome](https://github.com/TheAlgorithms/Javascript/blob/master/Slow-Fast-Pointer/IsLinkedListPalidrome.js) + ## Sorts * [BogoSort](https://github.com/TheAlgorithms/Javascript/blob/master/Sorts/BogoSort.js) * [BubbleSort](https://github.com/TheAlgorithms/Javascript/blob/master/Sorts/BubbleSort.js) diff --git a/Data-Structures/Linked-List/SinglyLinkList.js b/Data-Structures/Linked-List/SinglyLinkList.js index fbfdacf22c..ed17fef66d 100644 --- a/Data-Structures/Linked-List/SinglyLinkList.js +++ b/Data-Structures/Linked-List/SinglyLinkList.js @@ -56,6 +56,7 @@ var LinkedList = (function () { } // Increment the length this.length++ + return this } // Removes the node with the value as param @@ -179,6 +180,26 @@ var LinkedList = (function () { return currentNode.element } + LinkedList.prototype.addNodeToTail = function (node) { + var tail = this.head + for (var i = 0; i < this.length - 1; i++) { + tail = tail.next + } + tail.next = node + } + + LinkedList.prototype.print = function () { + var temp = this.head + var list = [] + + while (temp !== null) { + list.push(temp.element) + temp = temp.next + } + + console.log(list.join('->')) + } + // Function to view the LinkedList LinkedList.prototype.view = function () { var currentNode = this.head @@ -207,3 +228,5 @@ linklist.addAt(4, 15) console.log(linklist.indexOf(8)) console.log(linklist.size()) linklist.view() + +module.exports = LinkedList diff --git a/Slow-Fast-Pointers/HasLinkedListCycle.js b/Slow-Fast-Pointers/HasLinkedListCycle.js new file mode 100644 index 0000000000..546ec27447 --- /dev/null +++ b/Slow-Fast-Pointers/HasLinkedListCycle.js @@ -0,0 +1,38 @@ +// https://en.wikipedia.org/wiki/Cycle_detection + +/** + * Checks if there is a cycle in a LinkedList + * @param {LinkedList} head + * @returns {Boolean} + */ +function hasCycle (head) { + let slow = head + let fast = head + let isCircular = false + + while (fast !== null && fast.next !== null) { + slow = slow.next + fast = fast.next.next + if (slow === fast) { + isCircular = true + break + } + } + + return isCircular +} + +const LinkedList = require('../Data-Structures/Linked-List/SinglyLinkList') +const linkedList = new LinkedList() + .add(1) + .add(2) + .add(3) + .add(4) + .add(5) + .add(6) +const { head } = linkedList + +console.log('Has Cycle', hasCycle(head)) // false + +linkedList.addNodeToTail(head) +console.log('Has Cycle', hasCycle(head)) // true diff --git a/Slow-Fast-Pointers/IsLinkedListPalidrome.js b/Slow-Fast-Pointers/IsLinkedListPalidrome.js new file mode 100644 index 0000000000..4260c60d8f --- /dev/null +++ b/Slow-Fast-Pointers/IsLinkedListPalidrome.js @@ -0,0 +1,88 @@ +// https://en.wikipedia.org/wiki/Cycle_detection + +/** + * Finds the middle of a LinkedList + * @param {LinkedList} head + * @returns {LinkedList} + */ +function middleOfLinkedList (head) { + let slow = head + let fast = head + + while (fast !== null && fast.next !== null) { + slow = slow.next + fast = fast.next.next + } + + return slow +} + +/** + * Reverse the Linked list in place + * @param {LinkedList} current + * @returns {LinkedList} + */ +const reverseLinkedList = (current) => { + let prev = null + let next = null + + while (current !== null) { + next = current.next + current.next = prev + prev = current + current = next + } + + return prev +} + +/** + * Does a palindrome check between two nodes in a linkedList + * @param {LinkedList} first + * @param {LinkedList} second + * @returns {LinkedList} + */ +const compare = (first, second) => { + // Keep copy to reverse in place at the end + const copySecond = second + + // Compare the palindrome + while (first !== null && second !== null && first.element === second.element) { + first = first.next + second = second.next + } + + // Revert the reverse in place of the second half + reverseLinkedList(copySecond) + + return second +} + +/** + * Determines if a LinkedList is a palindrome + * @param {LinkedList} head + * @returns {Boolean} + */ +function isLinkedListPalindrome (head) { + if (head === null || head.next === null) return true + + const middle = middleOfLinkedList(head) + const secondHalfReversed = reverseLinkedList(middle) + const secondHalf = compare(head, secondHalfReversed) + + return (head === null || secondHalf === null) +} + +const LinkedList = require('../Data-Structures/Linked-List/SinglyLinkList') +const linkedList = new LinkedList() + .add(1) + .add(2) + .add(3) + .add(2) + .add(1) +const { head } = linkedList + +console.log('Is palindrome', isLinkedListPalindrome(head)) // true + +linkedList.add(1) +console.log('Is palindrome', isLinkedListPalindrome(head)) // false diff --git a/Slow-Fast-Pointers/LinkedListCycleLength.js b/Slow-Fast-Pointers/LinkedListCycleLength.js new file mode 100644 index 0000000000..8b061e6aa0 --- /dev/null +++ b/Slow-Fast-Pointers/LinkedListCycleLength.js @@ -0,0 +1,59 @@ +// https://en.wikipedia.org/wiki/Cycle_detection + +/** + * Counts the length of the cycle + * @param {LinkedList} slow + * @returns {Number} + */ +const calculateCycleLength = (slow) => { + let current = slow + let cycleLength = 0 + + while (true) { + current = current.next + cycleLength++ + + if (current === slow) break + } + + return cycleLength +} + +/** + * Determines the length of the cycle in the LinkedList + * @param {LinkedList} head + * @returns {Number} + */ +function cycleLength (head) { + let slow = head + let fast = head + let length = 0 + + while (fast !== null && fast.next !== null) { + slow = slow.next + fast = fast.next.next + + if (slow === fast) { + length = calculateCycleLength(slow) + break + } + } + + return length +} + +const LinkedList = require('../Data-Structures/Linked-List/SinglyLinkList') +const linkedList = new LinkedList() + .add(1) + .add(2) + .add(3) + .add(4) + .add(5) + .add(6) +const { head } = linkedList + +linkedList.addNodeToTail(head.next.next) +console.log('Cycle length', cycleLength(head)) // 4 + +linkedList.addNodeToTail(head.next.next.next) +console.log('Cycle length', cycleLength(head)) // 3 diff --git a/Slow-Fast-Pointers/MiddleOfLinkedList.js b/Slow-Fast-Pointers/MiddleOfLinkedList.js new file mode 100644 index 0000000000..c0cb8c2463 --- /dev/null +++ b/Slow-Fast-Pointers/MiddleOfLinkedList.js @@ -0,0 +1,29 @@ +// https://en.wikipedia.org/wiki/Cycle_detection + +/** + * Finds the middle of a LinkedList + * @param {LinkedList} head + * @returns {LinkedList} + */ +function middleOfLinkedList (head) { + let slow = head + let fast = head + + while (fast !== null && fast.next !== null) { + slow = slow.next + fast = fast.next.next + } + + return slow +} + +const LinkedList = require('../Data-Structures/Linked-List/SinglyLinkList') +const linkedList = new LinkedList() + .add(1) + .add(2) + .add(3) + .add(4) + .add(5) +const { head } = linkedList + +console.log('Middle', middleOfLinkedList(head).element) // 3 diff --git a/Slow-Fast-Pointers/README.md b/Slow-Fast-Pointers/README.md new file mode 100644 index 0000000000..067c901511 --- /dev/null +++ b/Slow-Fast-Pointers/README.md @@ -0,0 +1 @@ +# Slow Fast Pointers diff --git a/Slow-Fast-Pointers/ReArrangeLinkedList.js b/Slow-Fast-Pointers/ReArrangeLinkedList.js new file mode 100644 index 0000000000..4fa88d0ef7 --- /dev/null +++ b/Slow-Fast-Pointers/ReArrangeLinkedList.js @@ -0,0 +1,82 @@ +// https://en.wikipedia.org/wiki/Cycle_detection + +/** + * Finds the middle of a LinkedList + * @param {LinkedList} head + * @returns {LinkedList} + */ +const middleOfLinkedList = (head) => { + let slow = head + let fast = head + + while (fast !== null && fast.next !== null) { + fast = fast.next.next + slow = slow.next + } + + return slow +} + +/** + * Reverse the Linked list in place + * @param {LinkedList} current + * @returns {LinkedList} + */ +const reverseLinkedList = (current) => { + let prev = null + let next = null + + while (current !== null) { + next = current.next + current.next = prev + prev = current + current = next + } + + return prev +} + +/** + * Merges the two LinkedList by alternating between the two + * @param {LinkedList} first + * @param {LinkedList} second + * @returns {LinkedList} + */ +const merge = (first, second) => { + while (first !== null && second !== null) { + let next = first.next + first.next = second + first = next + + next = second.next + second.next = first + second = next + } + + if (first !== null) first.next = null +} + +/** + * Rearranges the LinkedList in place by reversing the second half and merging the list back by alternating the values + * @param {LinkedList} head + */ +const reArrange = function (head) { + const middle = middleOfLinkedList(head) + const secondHalfReversed = reverseLinkedList(middle) + + merge(head, secondHalfReversed) +} + +const LinkedList = require('../Data-Structures/Linked-List/SinglyLinkList') +const linkedList = new LinkedList() + .add(1) + .add(2) + .add(3) + .add(4) + .add(5) + .add(6) +const { head } = linkedList + +linkedList.print() // 1->2->3->4->5->6 +reArrange(head) +linkedList.print() // 1->6->2->5->3->4 diff --git a/Slow-Fast-Pointers/StartOfLinkedListCycle.js b/Slow-Fast-Pointers/StartOfLinkedListCycle.js new file mode 100644 index 0000000000..9d7aba5cc8 --- /dev/null +++ b/Slow-Fast-Pointers/StartOfLinkedListCycle.js @@ -0,0 +1,93 @@ +// https://en.wikipedia.org/wiki/Cycle_detection + +/** + * Counts the length of the cycle + * @param {LinkedList} slow + * @returns {Number} + */ +const calculateCycleLength = (slow) => { + let current = slow + let cycleLength = 0 + + while (true) { + current = current.next + cycleLength++ + + if (current === slow) break + } + + return cycleLength +} + +/** + * Determines the length of the cycle in the LinkedList + * @param {LinkedList} head + * @returns {Number} + */ +const cycleLength = (head) => { + let slow = head + let fast = head + let length = 0 + + while (fast !== null && fast.next !== null) { + slow = slow.next + fast = fast.next.next + + if (slow === fast) { + length = calculateCycleLength(slow) + break + } + } + + return length +} + +/** + * Locates the start of the cycle + * @param {LinkedList} head + * @param {Number} length + */ +const findCycleStart = (head, length) => { + let first = head + let second = head + + // Move and decrement until the end is reached + while (length > 0) { + second = second.next + length-- + } + + // Move and increment until the start is reached + while (first !== second) { + first = first.next + second = second.next + } + + return first +} + +/** + * Determines the node that starts the cycle in LinkedList + * @param {LinkedList} head + * @returns {LinkedList} + */ +function cycleStart (head) { + const length = cycleLength(head) + return findCycleStart(head, length) +} + +const LinkedList = require('../Data-Structures/Linked-List/SinglyLinkList') +const linkedList = new LinkedList() + .add(1) + .add(2) + .add(3) + .add(4) + .add(5) + .add(6) +const { head } = linkedList + +linkedList.addNodeToTail(head.next.next) +console.log('Cycle start value', cycleStart(head).element) // 3 + +linkedList.addNodeToTail(head.next.next.next) +console.log('Cycle start value', cycleStart(head).element) // 4 diff --git a/package-lock.json b/package-lock.json index 9bcf2855d9..ab36c25dc9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3734,12 +3734,11 @@ }, "dependencies": { "ansi-styles": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.2.1.tgz", - "integrity": "sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "requires": { - "@types/color-name": "^1.1.1", "color-convert": "^2.0.1" } }, @@ -7594,9 +7593,9 @@ } }, "tslib": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.13.0.tgz", - "integrity": "sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q==", + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.0.tgz", + "integrity": "sha512-+Zw5lu0D9tvBMjGP8LpvMb0u2WW2QV3y+D8mO6J+cNzCYIN4sVy43Bf9vl92nqFahutN0I8zHa7cc4vihIshnw==", "dev": true }, "tunnel-agent": { diff --git a/package.json b/package.json index eea349d1b3..2079a1679c 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,9 @@ "description": "A repository for All algorithms implemented in Javascript (for educational purposes only)", "main": "", "scripts": { - "test": "jest --no-cache" + "test": "jest --no-cache", + "lint": "standard", + "lint:fix": "standard --fix" }, "author": "TheAlgorithms", "license": "GPL-3.0", @@ -16,7 +18,9 @@ "node-fetch": "2.6.1" }, "standard": { - "env": [ "jest" ] + "env": [ + "jest" + ] }, "devDependencies": { "babel-jest": "^26.3.0",