Skip to content

Commit

Permalink
Added doubly linked list (trekhleb#92)
Browse files Browse the repository at this point in the history
* Added doubly linked list

* improved doubly linked list coverage
  • Loading branch information
huzaima authored and trekhleb committed Jul 5, 2018
1 parent fef2aa7 commit 6b354ad
Show file tree
Hide file tree
Showing 5 changed files with 510 additions and 0 deletions.
211 changes: 211 additions & 0 deletions src/data-structures/doubly-linked-list/DoublyLinkedList.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
import DoublyLinkedListNode from './DoublyLinkedListNode';
import Comparator from './../../utils/comparator/Comparator';

export default class DoublyLinkedList {
/**
* @param {Function} [comparatorFunction]
*/
constructor(comparatorFunction) {
/** @var DoublyLinkedListNode */
this.head = null;

/** @var DoublyLinkedListNode */
this.tail = null;

this.compare = new Comparator(comparatorFunction);
}

/**
* @param {*} value
* @return {DoublyLinkedList}
*/
prepend(value) {
// Make new node to be a head.
const newNode = new DoublyLinkedListNode(value, this.head);

// If there is head, then it won't be head anymore
// Therefore, make its previous reference to be new node (new head)
// Then mark the new node as head
if (this.head) {
this.head.previous = newNode;
}
this.head = newNode;

// If there is no tail yet let's make new node a tail.
if (!this.tail) {
this.tail = newNode;
}

return this;
}

/**
* @param {*} value
* @return {DoublyLinkedList}
*/
append(value) {
const newNode = new DoublyLinkedListNode(value);

// If there is no head yet let's make new node a head.
if (!this.head) {
this.head = newNode;
this.tail = newNode;

return this;
}

// Attach new node to the end of linked list.
this.tail.next = newNode;

// Attach current tail to the new node's previous reference
newNode.previous = this.tail;

// Set new node to be the tail of linked list.
this.tail = newNode;

return this;
}

/**
* @param {*} value
* @return {DoublyLinkedListNode}
*/
delete(value) {
if (!this.head) {
return null;
}

let deletedNode = null;
let currentNode = this.head;

do {
if (this.compare.equal(currentNode.value, value)) {
deletedNode = currentNode;

if (deletedNode === this.head) {
// set head to second node, which will become new head
this.head = deletedNode.next;

// set new head's previous to null
if (this.head) {
this.head.previous = null;
}

// If all the nodes in list has same value that is passed as argument
// then all nodes will get deleted, therefore tail needs to be updated
if (deletedNode === this.tail) {
this.tail = null;
}
} else if (deletedNode === this.tail) {
// set tail to second last node, which will become new tail
this.tail = deletedNode.previous;
this.tail.next = null;
} else {
const previousNode = deletedNode.previous;
const nextNode = deletedNode.next;
previousNode.next = nextNode;
nextNode.previous = previousNode;
}
}

currentNode = currentNode.next;
} while (currentNode);

return deletedNode;
}

/**
* @param {Object} findParams
* @param {*} findParams.value
* @param {function} [findParams.callback]
* @return {DoublyLinkedListNode}
*/
find({ value = undefined, callback = undefined }) {
if (!this.head) {
return null;
}

let currentNode = this.head;

while (currentNode) {
// If callback is specified then try to find node by callback.
if (callback && callback(currentNode.value)) {
return currentNode;
}

// If value is specified then try to compare by value..
if (value !== undefined && this.compare.equal(currentNode.value, value)) {
return currentNode;
}

currentNode = currentNode.next;
}

return null;
}

/**
* @return {DoublyLinkedListNode}
*/
deleteTail() {
if (!this.tail) {
return null;
} else if (this.head === this.tail) {
const deletedTail = this.tail;
this.head = null;
this.tail = null;

return deletedTail;
}

const deletedTail = this.tail;
this.tail = this.tail.previous;
this.tail.next = null;

return deletedTail;
}

/**
* @return {DoublyLinkedListNode}
*/
deleteHead() {
if (!this.head) {
return null;
}

const deletedHead = this.head;

if (this.head.next) {
this.head = this.head.next;
this.head.previous = null;
} else {
this.head = null;
this.tail = null;
}

return deletedHead;
}

/**
* @return {DoublyLinkedListNode[]}
*/
toArray() {
const nodes = [];

let currentNode = this.head;
while (currentNode) {
nodes.push(currentNode);
currentNode = currentNode.next;
}

return nodes;
}

/**
* @param {function} [callback]
* @return {string}
*/
toString(callback) {
return this.toArray().map(node => node.toString(callback)).toString();
}
}
11 changes: 11 additions & 0 deletions src/data-structures/doubly-linked-list/DoublyLinkedListNode.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export default class DoublyLinkedListNode {
constructor(value, next = null, previous = null) {
this.value = value;
this.next = next;
this.previous = previous;
}

toString(callback) {
return callback ? callback(this.value) : `${this.value}`;
}
}
10 changes: 10 additions & 0 deletions src/data-structures/doubly-linked-list/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Doubly Linked List

In computer science, a doubly linked list is a linked data structure that consists of a set of sequentially linked records called nodes. Each node contains two fields, called links, that are references to the previous and to the next node in the sequence of nodes. The beginning and ending nodes' previous and next links, respectively, point to some kind of terminator, typically a sentinel node or null, to facilitate traversal of the list. If there is only one sentinel node, then the list is circularly linked via the sentinel node. It can be conceptualized as two singly linked lists formed from the same data items, but in opposite sequential orders.

![Doubly Linked List](https://upload.wikimedia.org/wikipedia/commons/5/5e/Doubly-linked-list.svg)

## References

- [Wikipedia](https://en.wikipedia.org/wiki/Doubly_linked_list)
- [YouTube](https://www.youtube.com/watch?v=JdQeNxWCguQ)
Loading

0 comments on commit 6b354ad

Please sign in to comment.