LinkedList.js
import LinkedListNode from './LinkedListNode';
import Comparator from '../../utils/comparator/Comparator';
export default class LinkedList {
/**
* @param {Function} [comparatorFunction]
*/
constructor(comparatorFunction) {
/** @var LinkedListNode */
this.head = null; //初始化this.head指针用于维护链表头节点
/** @var LinkedListNode */
this.tail = null; //初始化this.head指针用于维护链表尾节点
this.compare = new Comparator(comparatorFunction); //导入自定义的比较函数
}
/**
* @param {*} value
* @return {LinkedList}
*/
prepend(value) { // 用于在链表头之前插入数据
// Make new node to be a head.
//创建一个新链表节点的同时,传入this.head作为其next指针(新插入的节点将指向原先的头节点),如果链表原来为空,则传入null
const newNode = new LinkedListNode(value, this.head);
this.head = newNode; //将this.head置于当前节点
// If there is no tail yet let's make new node a tail.
if (!this.tail) {
this.tail = newNode; // 如果this.tail指针没有指向,则将其指向tail,(说明原来是个空链表)
}
return this;
}
/**
* @param {*} value
* @return {LinkedList}
*/
append(value) { // 用于在链表尾部
const newNode = new LinkedListNode(value); //创建一个新的链表节点,单向链表不用考虑前面的指针,next指针默认为null,只需传入value
//如果是空链表,则将this.head/this.tail指针都指向该新节点
if (!this.head) {
this.head = newNode;
this.tail = newNode;
return this; //函数执行的结果返回实例对象。。都是这样。。在这里面
}
//链表不为空,则将链表末尾节点的next指向新节点,this.tail指针指向该节点,this.head指针不用动
this.tail.next = newNode;
this.tail = newNode;
return this;
}
/**
* @param {*} value
* @param {number} index
* @return {LinkedList}
*/
insert(value, rawIndex) {
const index = rawIndex < 0 ? 0 : rawIndex; //如果索引为负,置0,默认从最开始最前面插入
if (index === 0) {
this.prepend(value);
} else {
let count = 1;
let currentNode = this.head; //指针从表头开始遍历
const newNode = new LinkedListNode(value);
while (currentNode) {
if (count === index) break; //移动指针到对应索引位置的节点
currentNode = currentNode.next;
count += 1;
}
if (currentNode) { //索引没有超出链表索引,所以在指定节点后插入新的链表节点
newNode.next = currentNode.next;
currentNode.next = newNode;
} else {
if (this.tail) { //当前索引超出了链表索引
this.tail.next = newNode;
this.tail = newNode;
} else { //这种情况是空链表
this.head = newNode;
this.tail = newNode;
}
}
}
return this;
}
/**
* @param {*} value
* @return {LinkedListNode}
*/
delete(value) { //首先明确一点,delete(value)是会删除链表中的所有value,然后可以开始了
if (!this.head) { //说明是空数组,返回null
return null;
}
let deletedNode = null; //初始化delete,默认要删除的值不存在
// 如果必须删除头,则将与头不同的下一个节点作为一个新的头。
while (this.head && this.compare.equal(this.head.value, value)) {
deletedNode = this.head; //这是为了返回你删除的节点的obj
this.head = this.head.next; //如果要删除头,将this.head指向下一个node节点,头结点就被排除在链表外了
}
//不用删除头,走到这一步,说明不用删除头节点 or 头结点被while循环给干完了,现在的头节点不用删
let currentNode = this.head; //currentNode为当下this.head所指节点
if (currentNode !== null) { //节点存在,
// 如果必须删除下一个节点,则使下一个节点成为下一个节点。
while (currentNode.next) { //因为当下头结点不用删,直接开始currentNode.next的节点值的比较
if (this.compare.equal(currentNode.next.value, value)) {
deletedNode = currentNode.next; //这是为了返回你删除的节点的obj
currentNode.next = currentNode.next.next; //删除下一个节点
} else {
currentNode = currentNode.next; //将当前节点移动到下一个节点
}
}
}
// Check if tail must be deleted.
if (this.compare.equal(this.tail.value, value)) { //我不知道这一步有什么意义,不要这一步好像也可以。。。pass了
this.tail = currentNode;
}
return deletedNode;
}
/**
* @param {Object} findParams
* @param {*} findParams.value
* @param {function} [findParams.callback]
* @return {LinkedListNode}
*/
find({ value = undefined, callback = undefined }) { //这里传入的是一个配置对象
if (!this.head) { //说明是空链表
return null;
}
let currentNode = this.head;
while (currentNode) {
// 如果指定了回调函数,就通过回调函数来查找值
if (callback && callback(currentNode.value)) { //fn传入value,根据返回的值判断是否查找到
return currentNode;
}
// 没有回调函数就通过值的比较来查找值
if (value !== undefined && this.compare.equal(currentNode.value, value)) {
return currentNode;
}
currentNode = currentNode.next;
}
return null;
}
/**
* @return {LinkedListNode}
*/
deleteTail() {
const deletedTail = this.tail; //默认删除的未尾节点,并返回
if (this.head === this.tail) {
// 链表里只有一个节点
this.head = null;
this.tail = null;
return deletedTail;
}
// 如果链表中有很多节点…
// 回退到最后一个节点,删除最后一个节点之前的“下一个”链接。
let currentNode = this.head;
while (currentNode.next) {
if (!currentNode.next.next) { //如果没有下下个节点,下个节点就是尾节点
currentNode.next = null; //将当前节点的next节点置为null
} else {
currentNode = currentNode.next; //不然就将当前节点往后移
}
}
this.tail = currentNode; //没有节点就是返回null了
return deletedTail;
}
/**
* @return {LinkedListNode}
*/
deleteHead() {
if (!this.head) {
return null;
}
const deletedHead = this.head;
if (this.head.next) { //...很前面差不多
this.head = this.head.next;
} else {
this.head = null;
this.tail = null;
}
return deletedHead;
}
/**
* @param {*[]} values - Array of values that need to be converted to linked list.
* @return {LinkedList}
*/
fromArray(values) {
values.forEach((value) => this.append(value)); //输入数组,转成链表
return this;
}
/**
* @return {LinkedListNode[]}
*/
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();
}
/**
* 反转链表
* @returns {LinkedList}
*/
reverse() {
let currNode = this.head;
let prevNode = null;
let nextNode = null;
while (currNode) {
// 不停的到下一个节点,将下一个节点指向当前节点
nextNode = currNode.next;
//第一次就是指向了null,后续就都是指向前一个节点
currNode.next = prevNode;
// 将prevNode和currNode节点向前移动一步,这个reverse画个图就很清晰了
prevNode = currNode;
currNode = nextNode; //当currNode是null时,说明已经完成了尾节点的指针指向,因为,pervNode指向了null
}
// Reset head and tail.
this.tail = this.head;
this.head = prevNode;
return this;
}
}
LinkedListNode.js
export default class LinkedListNode {
constructor(value, next = null) {
this.value = value;
this.next = next;
}
toString(callback) {
return callback ? callback(this.value) : `${this.value}`;
}
}
Comparator.js
export default class Comparator {
/**
* Constructor.
* @param {function(a: *, b: *)} [compareFunction] - It may be custom compare function that, let's
* say may compare custom objects together.
* 它可能是一个自定义的比较函数,比如说,可以把自定义的对象比较在一起
*/
constructor(compareFunction) {
this.compare = compareFunction || Comparator.defaultCompareFunction;
}
/**
* Default comparison function. It just assumes that "a" and "b" are strings or numbers.
* @param {(string|number)} a
* @param {(string|number)} b
* @returns {number}
*/
static defaultCompareFunction(a, b) {
if (a === b) {
return 0;
}
return a < b ? -1 : 1;
}
/**
* Checks if two variables are equal.
* @param {*} a
* @param {*} b
* @return {boolean}
*/
equal(a, b) {
return this.compare(a, b) === 0;
}
/**
* Checks if variable "a" is less than "b".
* @param {*} a
* @param {*} b
* @return {boolean}
*/
lessThan(a, b) {
return this.compare(a, b) < 0;
}
/**
* Checks if variable "a" is greater than "b".
* @param {*} a
* @param {*} b
* @return {boolean}
*/
greaterThan(a, b) {
return this.compare(a, b) > 0;
}
/**
* Checks if variable "a" is less than or equal to "b".
* @param {*} a
* @param {*} b
* @return {boolean}
*/
lessThanOrEqual(a, b) {
return this.lessThan(a, b) || this.equal(a, b);
}
/**
* Checks if variable "a" is greater than or equal to "b".
* @param {*} a
* @param {*} b
* @return {boolean}
*/
greaterThanOrEqual(a, b) {
return this.greaterThan(a, b) || this.equal(a, b);
}
/**
* Reverses the comparison order.
*/
reverse() {
const compareOriginal = this.compare;
this.compare = (a, b) => compareOriginal(b, a);
}
}