Files
JavaScript/Data-Structures/Linked-List/SinglyLinkedList.js
Roland Hummel 86d333ee94 feat: Test running overhaul, switch to Prettier & reformat everything (#1407)
* chore: Switch to Node 20 + Vitest

* chore: migrate to vitest mock functions

* chore: code style (switch to prettier)

* test: re-enable long-running test

Seems the switch to Node 20 and Vitest has vastly improved the code's and / or the test's runtime!

see #1193

* chore: code style

* chore: fix failing tests

* Updated Documentation in README.md

* Update contribution guidelines to state usage of Prettier

* fix: set prettier printWidth back to 80

* chore: apply updated code style automatically

* fix: set prettier line endings to lf again

* chore: apply updated code style automatically

---------

Co-authored-by: github-actions <${GITHUB_ACTOR}@users.noreply.github.com>
Co-authored-by: Lars Müller <34514239+appgurueu@users.noreply.github.com>
2023-10-04 02:38:19 +05:30

306 lines
7.6 KiB
JavaScript

/* SinglyLinkedList!!
* A linked list is similar to an array, it holds a list of values.
* However, links in a linked list do not have indexes. With
* a linked list you do not need to predetermine its size as
* it grows and shrinks as it is edited. This is an example of
* a singly linked list.
*/
// Methods - size, head, addLast, addFirst, addAt, removeFirst, removeLast, remove, removeAt, indexOf, isEmpty, elementAt, findMiddle, get, clean, rotateListRight
class Node {
constructor(data) {
this.data = data
this.next = null
}
}
class LinkedList {
constructor(listOfValues) {
this.headNode = null
this.tailNode = null
this.length = 0
if (listOfValues instanceof Array) {
for (const value of listOfValues) {
this.addLast(value)
}
}
}
// initiates the currentNode and currentIndex and return as an object
initiateNodeAndIndex() {
return { currentNode: this.headNode, currentIndex: 0 }
}
// Returns length
size() {
return this.length
}
// Returns the head
head() {
return this.headNode?.data ?? null
}
// Returns the tail
tail() {
return this.tailNode?.data ?? null
}
// Return if the list is empty
isEmpty() {
return this.length === 0
}
// add a node at last it to linklist
addLast(element) {
// Check if its the first element
if (this.headNode === null) {
return this.addFirst(element)
}
const node = new Node(element)
// Adding node at the end of the list and increase the length
this.tailNode.next = node
this.tailNode = node
this.length++
return this.size()
}
// add a node at first it to linklist
addFirst(element) {
const node = new Node(element)
// Check if its the first element
if (this.headNode === null) {
this.tailNode = node
}
// Adding node at the start of the list and increase the length
node.next = this.headNode
this.headNode = node
this.length++
return this.size()
}
// remove the first from the linklist
removeFirst() {
// Check if head is null
if (this.headNode === null) {
return null
}
// Removing node at the start of the list and decrease the length
const removedNode = this.headNode
this.headNode = this.headNode.next
this.length--
// Check if the list is empty
if (this.isEmpty()) {
this.tailNode = null
}
return removedNode?.data
}
// remove the last from the linklist
removeLast() {
if (this.isEmpty()) return null
// Check if there is only one element
if (this.length === 1) {
return this.removeFirst()
}
// Removing node at the end of the list and decrease the length
const removedNode = this.tailNode
let { currentNode } = this.initiateNodeAndIndex()
while (currentNode.next.next) {
currentNode = currentNode.next
}
currentNode.next = null
this.tailNode = currentNode
this.length--
return removedNode.data
}
// Removes the node with the value as param
remove(element) {
if (this.isEmpty()) return null
let { currentNode } = this.initiateNodeAndIndex()
let removedNode = null
// Check if the head node is the element to remove
if (currentNode.data === element) {
return this.removeFirst()
}
// Check if the tail node is the element to remove
if (this.tailNode.data === element) {
return this.removeLast()
}
// Check which node is the node to remove
while (currentNode.next) {
if (currentNode.next.data === element) {
removedNode = currentNode.next
currentNode.next = currentNode.next.next
this.length--
return removedNode.data
}
currentNode = currentNode.next
}
return removedNode?.data || null
}
// Returns the index of the element passed as param otherwise -1
indexOf(element) {
if (this.isEmpty()) return -1
let { currentNode, currentIndex } = this.initiateNodeAndIndex()
while (currentNode) {
if (currentNode.data === element) {
return currentIndex
}
currentNode = currentNode.next
currentIndex++
}
return -1
}
// Returns the element at an index
elementAt(index) {
if (index >= this.length || index < 0) {
throw new RangeError('Out of Range index')
}
let { currentIndex, currentNode } = this.initiateNodeAndIndex()
while (currentIndex < index) {
currentIndex++
currentNode = currentNode.next
}
return currentNode.data
}
// Adds the element at specified index
addAt(index, element) {
// Check if index is out of bounds of list
if (index > this.length || index < 0) {
throw new RangeError('Out of Range index')
}
if (index === 0) return this.addFirst(element)
if (index === this.length) return this.addLast(element)
let { currentIndex, currentNode } = this.initiateNodeAndIndex()
const node = new Node(element)
while (currentIndex !== index - 1) {
currentIndex++
currentNode = currentNode.next
}
// Adding the node at specified index
const tempNode = currentNode.next
currentNode.next = node
node.next = tempNode
// Incrementing the length
this.length++
return this.size()
}
// Removes the node at specified index
removeAt(index) {
// Check if index is present in list
if (index < 0 || index >= this.length) {
throw new RangeError('Out of Range index')
}
if (index === 0) return this.removeFirst()
if (index === this.length - 1) return this.removeLast()
let { currentIndex, currentNode } = this.initiateNodeAndIndex()
while (currentIndex !== index - 1) {
currentIndex++
currentNode = currentNode.next
}
const removedNode = currentNode.next
currentNode.next = currentNode.next.next
this.length--
return removedNode.data
}
// Returns a reference to middle node of linked list
findMiddle() {
// If there are two middle nodes, return the second middle node.
let fast = this.headNode
let slow = this.headNode
while (fast !== null && fast.next !== null) {
fast = fast.next.next
slow = slow.next
}
return slow
}
// make the linkedList Empty
clean() {
this.headNode = null
this.tailNode = null
this.length = 0
}
// Method to get the LinkedList
get() {
const list = []
let { currentNode } = this.initiateNodeAndIndex()
while (currentNode) {
list.push(currentNode.data)
currentNode = currentNode.next
}
return list
}
// Method for Rotating a List to the right by k places
rotateListRight(k) {
if (!this.headNode) return
let current = this.headNode
let tail = this.tailNode
let count = 1
while (current.next) {
count++
current = current.next
}
current.next = this.headNode
tail = current
k %= count
while (count - k > 0) {
tail = tail.next
count--
}
this.headNode = tail.next
tail.next = null
}
// Method to iterate over the LinkedList
iterator() {
let { currentNode } = this.initiateNodeAndIndex()
if (currentNode === null) return -1
const iterate = function* () {
while (currentNode) {
yield currentNode.data
currentNode = currentNode.next
}
}
return iterate()
}
// Method to log the LinkedList
log() {
console.log(JSON.stringify(this.headNode, null, 2))
}
// Method to reverse the LinkedList
reverse() {
let head = this.headNode
let prev = null
let next = null
while (head) {
next = head.next
head.next = prev
prev = head
head = next
}
this.tailNode = this.headNode
this.headNode = prev
}
}
export { Node, LinkedList }