mirror of
https://github.com/TheAlgorithms/JavaScript.git
synced 2025-07-05 00:01:37 +08:00

* 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>
306 lines
7.6 KiB
JavaScript
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 }
|