first commit
This commit is contained in:
474
build/node_modules/dbly-linked-list/index.js
generated
vendored
Normal file
474
build/node_modules/dbly-linked-list/index.js
generated
vendored
Normal file
@@ -0,0 +1,474 @@
|
||||
/**
|
||||
* @fileOverview Implementation of a doubly linked-list data structure
|
||||
* @author Jason S. Jones
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
var isEqual = require('lodash.isequal');
|
||||
var Node = require('./lib/list-node');
|
||||
var Iterator = require('./lib/iterator');
|
||||
|
||||
/**************************************************
|
||||
* Doubly linked list class
|
||||
*
|
||||
* Implementation of a doubly linked list data structure. This
|
||||
* implementation provides the general functionality of adding nodes to
|
||||
* the front or back of the list, as well as removing node from the front
|
||||
* or back. This functionality enables this implemention to be the
|
||||
* underlying data structure for the more specific stack or queue data
|
||||
* structure.
|
||||
*
|
||||
***************************************************/
|
||||
|
||||
/**
|
||||
* Creates a LinkedList instance. Each instance has a head node, a tail
|
||||
* node and a size, which represents the number of nodes in the list.
|
||||
*
|
||||
* @constructor
|
||||
*/
|
||||
function DoublyLinkedList() {
|
||||
this.head = null;
|
||||
this.tail = null;
|
||||
this.size = 0;
|
||||
|
||||
// add iterator as a property of this list to share the same
|
||||
// iterator instance with all other methods that may require
|
||||
// its use. Note: be sure to call this.iterator.reset() to
|
||||
// reset this iterator to point the head of the list.
|
||||
this.iterator = new Iterator(this);
|
||||
}
|
||||
|
||||
/* Functions attached to the Linked-list prototype. All linked-list
|
||||
* instances will share these methods, meaning there will NOT be copies
|
||||
* made for each instance. This will be a huge memory savings since there
|
||||
* may be several different linked lists.
|
||||
*/
|
||||
DoublyLinkedList.prototype = {
|
||||
|
||||
/**
|
||||
* Creates a new Node object with 'data' assigned to the node's data
|
||||
* property
|
||||
*
|
||||
* @param {object|string|number} data The data to initialize with the
|
||||
* node
|
||||
* @returns {object} Node object intialized with 'data'
|
||||
*/
|
||||
createNewNode: function(data) {
|
||||
return new Node(data);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the first node in the list, commonly referred to as the
|
||||
* 'head' node
|
||||
*
|
||||
* @returns {object} the head node of the list
|
||||
*/
|
||||
getHeadNode: function() {
|
||||
return this.head;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the last node in the list, commonly referred to as the
|
||||
* 'tail'node
|
||||
*
|
||||
* @returns {object} the tail node of the list
|
||||
*/
|
||||
getTailNode: function() {
|
||||
return this.tail;
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines if the list is empty
|
||||
*
|
||||
* @returns {boolean} true if the list is empty, false otherwise
|
||||
*/
|
||||
isEmpty: function() {
|
||||
return (this.size === 0);
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the size of the list, or number of nodes
|
||||
*
|
||||
* @returns {number} the number of nodes in the list
|
||||
*/
|
||||
getSize: function() {
|
||||
return this.size;
|
||||
},
|
||||
|
||||
/**
|
||||
* Clears the list of all nodes/data
|
||||
*/
|
||||
clear: function () {
|
||||
while (!this.isEmpty()) {
|
||||
this.remove();
|
||||
}
|
||||
},
|
||||
|
||||
//################## INSERT methods ####################
|
||||
|
||||
/**
|
||||
* Inserts a node with the provided data to the end of the list
|
||||
*
|
||||
* @param {object|string|number} data The data to initialize with the
|
||||
* node
|
||||
* @returns {boolean} true if insert operation was successful
|
||||
*/
|
||||
insert: function(data) {
|
||||
var newNode = this.createNewNode(data);
|
||||
if (this.isEmpty()) {
|
||||
this.head = this.tail = newNode;
|
||||
} else {
|
||||
this.tail.next = newNode;
|
||||
newNode.prev = this.tail;
|
||||
this.tail = newNode;
|
||||
}
|
||||
this.size += 1;
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Inserts a node with the provided data to the front of the list
|
||||
*
|
||||
* @param {object|string|number} data The data to initialize with the
|
||||
* node
|
||||
* @returns {boolean} true if insert operation was successful
|
||||
*/
|
||||
insertFirst: function(data) {
|
||||
if (this.isEmpty()) {
|
||||
this.insert(data);
|
||||
} else {
|
||||
var newNode = this.createNewNode(data);
|
||||
|
||||
newNode.next = this.head;
|
||||
this.head.prev = newNode;
|
||||
this.head = newNode;
|
||||
|
||||
this.size += 1;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Inserts a node with the provided data at the index indicated.
|
||||
*
|
||||
* @param {number} index The index in the list to insert the new node
|
||||
* @param {object|string|number} data The data to initialize with the node
|
||||
*/
|
||||
insertAt: function (index, data) {
|
||||
var current = this.getHeadNode(),
|
||||
newNode = this.createNewNode(data),
|
||||
position = 0;
|
||||
|
||||
// check for index out-of-bounds
|
||||
if (index < 0 || index > this.getSize() - 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// if index is 0, we just need to insert the first node
|
||||
if (index === 0) {
|
||||
this.insertFirst(data);
|
||||
return true;
|
||||
}
|
||||
|
||||
while (position < index) {
|
||||
current = current.next;
|
||||
position += 1;
|
||||
}
|
||||
|
||||
current.prev.next = newNode;
|
||||
newNode.prev = current.prev;
|
||||
current.prev = newNode;
|
||||
newNode.next = current;
|
||||
|
||||
this.size += 1;
|
||||
|
||||
return true;
|
||||
},
|
||||
|
||||
/**
|
||||
* Inserts a node before the first node containing the provided data
|
||||
*
|
||||
* @param {object|string|number} nodeData The data of the node to
|
||||
* find to insert the new node before
|
||||
* @param {object|string|number} dataToInsert The data to initialize with the node
|
||||
* @returns {boolean} true if insert operation was successful
|
||||
*/
|
||||
insertBefore: function (nodeData, dataToInsert) {
|
||||
var index = this.indexOf(nodeData);
|
||||
return this.insertAt(index, dataToInsert);
|
||||
},
|
||||
|
||||
/**
|
||||
* Inserts a node after the first node containing the provided data
|
||||
*
|
||||
* @param {object|string|number} nodeData The data of the node to
|
||||
* find to insert the new node after
|
||||
* @param {object|string|number} dataToInsert The data to initialize with the node
|
||||
* @returns {boolean} true if insert operation was successful
|
||||
*/
|
||||
insertAfter: function (nodeData, dataToInsert) {
|
||||
var index = this.indexOf(nodeData);
|
||||
var size = this.getSize();
|
||||
|
||||
// check if we want to insert new node after the tail node
|
||||
if (index + 1 === size) {
|
||||
|
||||
// if so, call insert, which will append to the end by default
|
||||
return this.insert(dataToInsert);
|
||||
|
||||
} else {
|
||||
|
||||
// otherwise, increment the index and insert there
|
||||
return this.insertAt(index + 1, dataToInsert);
|
||||
}
|
||||
},
|
||||
|
||||
//################## REMOVE methods ####################
|
||||
|
||||
/**
|
||||
* Removes the tail node from the list
|
||||
*
|
||||
* There is a significant performance improvement with the operation
|
||||
* over its singly linked list counterpart. The mere fact of having
|
||||
* a reference to the previous node improves this operation from O(n)
|
||||
* (in the case of singly linked list) to O(1).
|
||||
*
|
||||
* @returns the node that was removed
|
||||
*/
|
||||
remove: function() {
|
||||
if (this.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// get handle for the tail node
|
||||
var nodeToRemove = this.getTailNode();
|
||||
|
||||
// if there is only one node in the list, set head and tail
|
||||
// properties to null
|
||||
if (this.getSize() === 1) {
|
||||
this.head = null;
|
||||
this.tail = null;
|
||||
|
||||
// more than one node in the list
|
||||
} else {
|
||||
this.tail = this.getTailNode().prev;
|
||||
this.tail.next = null;
|
||||
}
|
||||
this.size -= 1;
|
||||
|
||||
return nodeToRemove;
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes the head node from the list
|
||||
*
|
||||
* @returns the node that was removed
|
||||
*/
|
||||
removeFirst: function() {
|
||||
if (this.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var nodeToRemove;
|
||||
|
||||
if (this.getSize() === 1) {
|
||||
nodeToRemove = this.remove();
|
||||
} else {
|
||||
nodeToRemove = this.getHeadNode();
|
||||
this.head = this.head.next;
|
||||
this.head.prev = null;
|
||||
this.size -= 1;
|
||||
}
|
||||
|
||||
return nodeToRemove;
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes the node at the index provided
|
||||
*
|
||||
* @param {number} index The index of the node to remove
|
||||
* @returns the node that was removed
|
||||
*/
|
||||
removeAt: function (index) {
|
||||
var nodeToRemove = this.findAt(index);
|
||||
|
||||
// check for index out-of-bounds
|
||||
if (index < 0 || index > this.getSize() - 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// if index is 0, we just need to remove the first node
|
||||
if (index === 0) {
|
||||
return this.removeFirst();
|
||||
}
|
||||
|
||||
// if index is size-1, we just need to remove the last node,
|
||||
// which remove() does by default
|
||||
if (index === this.getSize() - 1) {
|
||||
return this.remove();
|
||||
}
|
||||
|
||||
nodeToRemove.prev.next = nodeToRemove.next;
|
||||
nodeToRemove.next.prev = nodeToRemove.prev;
|
||||
nodeToRemove.next = nodeToRemove.prev = null;
|
||||
|
||||
this.size -= 1;
|
||||
|
||||
return nodeToRemove;
|
||||
},
|
||||
|
||||
/**
|
||||
* Removes the first node that contains the data provided
|
||||
*
|
||||
* @param {object|string|number} nodeData The data of the node to remove
|
||||
* @returns the node that was removed
|
||||
*/
|
||||
removeNode: function (nodeData) {
|
||||
var index = this.indexOf(nodeData);
|
||||
return this.removeAt(index);
|
||||
},
|
||||
|
||||
//################## FIND methods ####################
|
||||
|
||||
/**
|
||||
* Returns the index of the first node containing the provided data. If
|
||||
* a node cannot be found containing the provided data, -1 is returned.
|
||||
*
|
||||
* @param {object|string|number} nodeData The data of the node to find
|
||||
* @returns the index of the node if found, -1 otherwise
|
||||
*/
|
||||
indexOf: function(nodeData) {
|
||||
this.iterator.reset();
|
||||
var current;
|
||||
|
||||
var index = 0;
|
||||
|
||||
// iterate over the list (keeping track of the index value) until
|
||||
// we find the node containg the nodeData we are looking for
|
||||
while (this.iterator.hasNext()) {
|
||||
current = this.iterator.next();
|
||||
if (isEqual(current.getData(), nodeData)) {
|
||||
return index;
|
||||
}
|
||||
index += 1;
|
||||
}
|
||||
|
||||
// only get here if we didn't find a node containing the nodeData
|
||||
return -1;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the fist node containing the provided data. If a node
|
||||
* cannot be found containing the provided data, -1 is returned.
|
||||
*
|
||||
* @param {object|string|number} nodeData The data of the node to find
|
||||
* @returns the node if found, -1 otherwise
|
||||
*/
|
||||
find: function(nodeData) {
|
||||
// start at the head of the list
|
||||
this.iterator.reset();
|
||||
var current;
|
||||
|
||||
// iterate over the list until we find the node containing the data
|
||||
// we are looking for
|
||||
while (this.iterator.hasNext()) {
|
||||
current = this.iterator.next();
|
||||
if (isEqual(current.getData(), nodeData)) {
|
||||
return current;
|
||||
}
|
||||
}
|
||||
|
||||
// only get here if we didn't find a node containing the nodeData
|
||||
return -1;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns the node at the location provided by index
|
||||
*
|
||||
* @param {number} index The index of the node to return
|
||||
* @returns the node located at the index provided.
|
||||
*/
|
||||
findAt: function(index) {
|
||||
// if idx is out of bounds or fn called on empty list, return -1
|
||||
if (this.isEmpty() || index > this.getSize() - 1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// else, loop through the list and return the node in the
|
||||
// position provided by idx. Assume zero-based positions.
|
||||
var node = this.getHeadNode();
|
||||
var position = 0;
|
||||
|
||||
while (position < index) {
|
||||
node = node.next;
|
||||
position += 1;
|
||||
}
|
||||
|
||||
return node;
|
||||
},
|
||||
|
||||
/**
|
||||
* Determines whether or not the list contains the provided nodeData
|
||||
*
|
||||
* @param {object|string|number} nodeData The data to check if the list
|
||||
* contains
|
||||
* @returns the true if the list contains nodeData, false otherwise
|
||||
*/
|
||||
contains: function (nodeData) {
|
||||
if (this.indexOf(nodeData) > -1) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
//################## UTILITY methods ####################
|
||||
|
||||
/**
|
||||
* Utility function to iterate over the list and call the fn provided
|
||||
* on each node, or element, of the list
|
||||
*
|
||||
* @param {object} fn The function to call on each node of the list
|
||||
* @param {bool} reverse Use or not reverse iteration (tail to head), default to false
|
||||
*/
|
||||
forEach: function(fn, reverse) {
|
||||
reverse = reverse || false;
|
||||
if (reverse) {
|
||||
this.iterator.reset_reverse();
|
||||
this.iterator.each_reverse(fn)
|
||||
} else {
|
||||
this.iterator.reset();
|
||||
this.iterator.each(fn);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns an array of all the data contained in the list
|
||||
*
|
||||
* @returns {array} the array of all the data from the list
|
||||
*/
|
||||
toArray: function() {
|
||||
var listArray = [];
|
||||
this.forEach(function(node) {
|
||||
listArray.push(node.getData());
|
||||
});
|
||||
|
||||
return listArray;
|
||||
},
|
||||
|
||||
/**
|
||||
* Interrupts iteration over the list
|
||||
*/
|
||||
interruptEnumeration: function() {
|
||||
this.iterator.interrupt();
|
||||
}
|
||||
};
|
||||
|
||||
module.exports = DoublyLinkedList;
|
||||
|
||||
}());
|
||||
Reference in New Issue
Block a user