279 lines
5.4 KiB
JavaScript
279 lines
5.4 KiB
JavaScript
|
/**
|
||
|
* Linked list.
|
||
|
*
|
||
|
* @example
|
||
|
*
|
||
|
* var LL = require('path-to-algorithms/src/data-structures/linked-list');
|
||
|
*
|
||
|
* var linkedList = new LL.LinkedList();
|
||
|
*
|
||
|
* linkedList.push({
|
||
|
* name: 'John',
|
||
|
* birthyear: 1981
|
||
|
* });
|
||
|
* linkedList.push({
|
||
|
* name: 'Pavlo',
|
||
|
* birthyear: 2000
|
||
|
* });
|
||
|
* linkedList.push({
|
||
|
* name: 'Garry',
|
||
|
* birthyear: 1989
|
||
|
* });
|
||
|
* linkedList.push({
|
||
|
* name: 'Derek',
|
||
|
* birthyear: 1990
|
||
|
* });
|
||
|
* linkedList.push({
|
||
|
* name: 'Ivan',
|
||
|
* birthyear: 1966
|
||
|
* });
|
||
|
*
|
||
|
* console.log(linkedList.shift().data); // { name: 'John', birthyear: 1981 }
|
||
|
* console.log(linkedList.pop().data); // { name: 'Ivan', birthyear: 1966 }
|
||
|
*
|
||
|
* @module data-structures/linked-list
|
||
|
*/
|
||
|
(function (exports) {
|
||
|
|
||
|
'use strict';
|
||
|
|
||
|
/**
|
||
|
* Linked list node.
|
||
|
*
|
||
|
* @public
|
||
|
* @constructor
|
||
|
* @param {Object} data Data of the node.
|
||
|
*/
|
||
|
exports.Node = function (data) {
|
||
|
/**
|
||
|
* Data of the node.
|
||
|
* @member {Object}
|
||
|
*/
|
||
|
this.data = data;
|
||
|
/**
|
||
|
* Next node.
|
||
|
* @member {Node}
|
||
|
*/
|
||
|
this.next = null;
|
||
|
/**
|
||
|
* Previous node.
|
||
|
* @member {Node}
|
||
|
*/
|
||
|
this.prev = null;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Linked list.
|
||
|
*
|
||
|
* @public
|
||
|
* @constructor
|
||
|
*/
|
||
|
exports.LinkedList = function () {
|
||
|
this.first = null;
|
||
|
this.last = null;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Add data to the end of linked list.
|
||
|
*
|
||
|
* @public
|
||
|
* @method
|
||
|
* @param {Object} data Data which should be added.
|
||
|
*/
|
||
|
exports.LinkedList.prototype.push = function (data) {
|
||
|
var node = new exports.Node(data);
|
||
|
if (this.first === null) {
|
||
|
this.first = this.last = node;
|
||
|
} else {
|
||
|
var temp = this.last;
|
||
|
this.last = node;
|
||
|
node.prev = temp;
|
||
|
temp.next = node;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Add data to the beginning of linked list.
|
||
|
*
|
||
|
* @public
|
||
|
* @method
|
||
|
* @param {Object} data Data which should be added.
|
||
|
*/
|
||
|
exports.LinkedList.prototype.unshift = function (data) {
|
||
|
var node = new exports.Node(data);
|
||
|
if (this.first === null) {
|
||
|
this.first = this.last = node;
|
||
|
} else {
|
||
|
var temp = this.first;
|
||
|
this.first = node;
|
||
|
node.next = temp;
|
||
|
temp.prev = node;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* In order traversal of the linked list.
|
||
|
*
|
||
|
* @public
|
||
|
* @method
|
||
|
* @param {Function} cb Callback which should be executed on each node.
|
||
|
*/
|
||
|
exports.LinkedList.prototype.inorder = function (cb) {
|
||
|
var temp = this.first;
|
||
|
while (temp) {
|
||
|
cb(temp);
|
||
|
temp = temp.next;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Remove data from the linked list.
|
||
|
*
|
||
|
* @public
|
||
|
* @method
|
||
|
* @param {Object} data Data which should be removed.
|
||
|
* @return {Boolean} Returns true if data has been removed.
|
||
|
*/
|
||
|
exports.LinkedList.prototype.remove = function (data) {
|
||
|
if (this.first === null) {
|
||
|
return false;
|
||
|
}
|
||
|
var temp = this.first;
|
||
|
var next;
|
||
|
var prev;
|
||
|
while (temp) {
|
||
|
if (temp.data === data) {
|
||
|
next = temp.next;
|
||
|
prev = temp.prev;
|
||
|
if (next) {
|
||
|
next.prev = prev;
|
||
|
}
|
||
|
if (prev) {
|
||
|
prev.next = next;
|
||
|
}
|
||
|
if (temp === this.first) {
|
||
|
this.first = next;
|
||
|
}
|
||
|
if (temp === this.last) {
|
||
|
this.last = prev;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
temp = temp.next;
|
||
|
}
|
||
|
return false;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Check if linked list contains cycle.
|
||
|
*
|
||
|
* @public
|
||
|
* @method
|
||
|
* @return {Boolean} Returns true if linked list contains cycle.
|
||
|
*/
|
||
|
exports.LinkedList.prototype.hasCycle = function () {
|
||
|
var fast = this.first;
|
||
|
var slow = this.first;
|
||
|
while (true) {
|
||
|
if (fast === null) {
|
||
|
return false;
|
||
|
}
|
||
|
fast = fast.next;
|
||
|
if (fast === null) {
|
||
|
return false;
|
||
|
}
|
||
|
fast = fast.next;
|
||
|
slow = slow.next;
|
||
|
if (fast === slow) {
|
||
|
return true;
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Return last node from the linked list.
|
||
|
*
|
||
|
* @public
|
||
|
* @method
|
||
|
* @return {Node} Last node.
|
||
|
*/
|
||
|
exports.LinkedList.prototype.pop = function () {
|
||
|
if (this.last === null) {
|
||
|
return null;
|
||
|
}
|
||
|
var temp = this.last;
|
||
|
this.last = this.last.prev;
|
||
|
return temp;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Return first node from the linked list.
|
||
|
*
|
||
|
* @public
|
||
|
* @method
|
||
|
* @return {Node} First node.
|
||
|
*/
|
||
|
exports.LinkedList.prototype.shift = function () {
|
||
|
if (this.first === null) {
|
||
|
return null;
|
||
|
}
|
||
|
var temp = this.first;
|
||
|
this.first = this.first.next;
|
||
|
return temp;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Reverses the linked list recursively
|
||
|
*
|
||
|
* @public
|
||
|
* @method
|
||
|
*/
|
||
|
exports.LinkedList.prototype.recursiveReverse = function () {
|
||
|
|
||
|
function inverse(current, next) {
|
||
|
if (!next) {
|
||
|
return;
|
||
|
}
|
||
|
inverse(next, next.next);
|
||
|
next.next = current;
|
||
|
}
|
||
|
|
||
|
if (!this.first) {
|
||
|
return;
|
||
|
}
|
||
|
inverse(this.first, this.first.next);
|
||
|
this.first.next = null;
|
||
|
var temp = this.first;
|
||
|
this.first = this.last;
|
||
|
this.last = temp;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Reverses the linked list iteratively
|
||
|
*
|
||
|
* @public
|
||
|
* @method
|
||
|
*/
|
||
|
exports.LinkedList.prototype.reverse = function () {
|
||
|
if (!this.first || !this.first.next) {
|
||
|
return;
|
||
|
}
|
||
|
var current = this.first.next;
|
||
|
var prev = this.first;
|
||
|
var temp;
|
||
|
while (current) {
|
||
|
temp = current.next;
|
||
|
current.next = prev;
|
||
|
prev.prev = current;
|
||
|
prev = current;
|
||
|
current = temp;
|
||
|
}
|
||
|
this.first.next = null;
|
||
|
this.last.prev = null;
|
||
|
temp = this.first;
|
||
|
this.first = prev;
|
||
|
this.last = temp;
|
||
|
};
|
||
|
|
||
|
})(typeof window === 'undefined' ? module.exports : window);
|