javascript - LinkedList
代码参考了 java 中的 LinkedList,函数名基本一致,基础上,增加了游标的设计。
/* eslint-disable */
/**
* 数组虽然方便,但是链表也有一定都存在价值;
* 比如:制作数据缓存,用户需要填写一份非常复杂的表单,用户发现错误,需要返回上一步。
*
* 代码参考自 java 的 LinkedList,做了一定的调整,会有少许差异。
*
* 增加游标的设计:
* 主要目的是为了缓存用户的操作记录,因此增加了一个游标(cursor),用于查看上一个、下一个元素。
* 游标的移动,不会改变元素的值。
* 相关函数:first/last/next/back
*
* @author Mr.css
*/
function LinkedList() {
let size = 0;
let first, last;
/**
* 一个用于在链表上移动的对象
* 动态调整链表的过程中,移除元素的时候,需要注意游标是否在该对象上。
*/
let cursor;
/**
* Tells if the argument is the index of an existing element.
*/
function isElementIndex(index) {
return index >= 0 && index < size;
}
/**
* Tells if the argument is the index of a valid position for an
* iterator or an add operation.
*/
function isPositionIndex(index) {
return index >= 0 && index <= size;
}
/**
* Constructs an IndexOutOfBoundsException detail message.
* Of the many possible refactorings of the error handling code,
* this "outlining" performs best with both server and client VMs.
*/
function outOfBoundsMsg(index) {
return "Index: " + index + ", Size: " + size;
}
function checkElementIndex(index) {
if (!isElementIndex(index))
throw outOfBoundsMsg(index);
}
function checkPositionIndex(index) {
if (!isPositionIndex(index))
throw outOfBoundsMsg(index);
}
function getNode(index) {
checkPositionIndex(index);
if (index < (size >> 1)) {
let x = first;
for (let i = 0; i < index; i++)
x = x.next;
return x;
} else {
let x = last;
for (let i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
/**
* Unlinks non-null first node f.
*/
function unlinkFirst(f) {
const element = f.item;
const next = f.next;
f.item = null;
f.next = null; // help GC
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
return element;
}
/**
* Unlinks non-null last node l.
*/
function unlinkLast(l) {
const element = l.item;
const prev = l.prev;
l.item = null;
l.prev = null; // help GC
last = prev;
if (prev == null)
first = null;
else
prev.next = null;
size--;
return element;
}
/**
* Unlinks non-null last node l.
*/
function unlink(x) {
const element = x.item;
const next = x.next;
const prev = x.prev;
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
return element;
}
/**
* Links e as first element.
*/
function linkFirst(e) {
const f = first;
const newNode = {prev: null, item: e, next: f};
first = newNode;
if (f === undefined)
last = newNode;
else
f.prev = newNode;
size++;
}
/**
* Links e as last element.
*/
function linkLast(e) {
const l = last;
const newNode = {prev: l, item: e, next: null};
last = newNode;
if (l === undefined)
first = newNode;
else
l.next = newNode;
size++;
}
/**
* Inserts element e before non-null Node succ.
*/
function linkBefore(e, succ) {
const pred = succ.prev;
const newNode = {prev: pred, pred: e, next: succ};
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
}
return {
/**
* Returns the first element in this list.
*
* @return the first element in this list
* @throws NoSuchElementException if this list is empty
*/
getFirst: function () {
const f = first;
if (f == null)
throw 'no such element';
return f.item;
},
/**
* Returns the last element in this list.
*
* @return the last element in this list
*/
getLast: function () {
const l = last;
if (l == null)
throw 'no such element';
return l.item;
},
/**
* Retrieves and removes the head (first element) of this list.
*
* @return the head of this list
* @throws NoSuchElementException if this list is empty
*/
removeFirst: function () {
const f = first;
if (f == null)
throw 'no such element';
return unlinkFirst(f);
},
/**
* Removes and returns the last element from this list.
*
* @return the last element from this list
* @throws NoSuchElementException if this list is empty
*/
removeLast: function () {
const l = last;
if (l == null)
throw 'no such element';
return unlinkLast(l);
},
/**
* Inserts the specified element at the beginning of this list.
*
* @param e the element to add
*/
addFirst: function (e) {
linkFirst(e);
},
/**
* Appends the specified element to the end of this list.
*
* <p>This method is equivalent to {@link #add}.
*
* @param e the element to add
*/
addLast: function (e) {
linkLast(e);
},
/**
* Returns true if this list contains the specified element.
* More formally, returns true if and only if this list contains
* at least one element {@code e} such that
* <tt>(o==null ? e==null : o.equals(e))</tt>.
*
* @param o element whose presence in this list is to be tested
* @return if this list contains the specified element
*/
contains: function (o) {
return indexOf(o) !== -1;
},
/**
* Returns the number of elements in this list.
*
* @return the number of elements in this list
*/
size: function () {
return size;
},
/**
* Appends the specified element to the end of this list.
*
* <p>This method is equivalent to {@link #addLast}.
*
* @param e element to be appended to this list
*/
add: function (e) {
linkLast(e);
return true;
},
/**
* Appends all of the elements in the specified collection to the end of
* this list, in the order that they are returned by the specified
* collection's iterator. The behavior of this operation is undefined if
* the specified collection is modified while the operation is in
* progress. (Note that this will occur if the specified collection is
* this list, and it's nonempty.)
*
* @param c collection containing elements to be added to this list
* @return true if this list changed as a result of the call
* @throws if the specified collection is null
*/
addAll: function (c) {
return this.addAllIn(size, c);
},
/**
* Inserts all of the elements in the specified collection into this
* list, starting at the specified position. Shifts the element
* currently at that position (if any) and any subsequent elements to
* the right (increases their indices). The new elements will appear
* in the list in the order that they are returned by the
* specified collection's iterator.
*
* @param index index at which to insert the first element
* from the specified collection
* @param a{[]} collection containing elements to be added to this list
* @return true if this list changed as a result of the call
* @throws IndexOutOfBoundsException {@inheritDoc}
* @throws if the specified collection is null
*/
addAllIn: function (index, a) {
checkPositionIndex(index);
let numNew = a.length;
if (numNew === 0)
return false;
let pred, succ;
if (index === size) {
succ = null;
pred = last;
} else {
succ = getNode(index);
pred = succ.prev;
}
for (let e in a) {
let newNode = {prev: pred, item: e, next: null};
if (pred == null)
first = newNode;
else
pred.next = newNode;
pred = newNode;
}
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;
return true;
},
/**
* Removes the first occurrence of the specified element from this list,
* if it is present. If this list does not contain the element, it is
* unchanged. More formally, removes the element with the lowest index
* {@code i} such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>
* (if such an element exists). Returns true if this list
* contained the specified element (or equivalently, if this list
* changed as a result of the call).
*
* @param o element to be removed from this list, if present
*/
remove: function (o) {
if (o == null) {
for (let x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (let x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
},
/**
* Returns the element at the specified position in this list.
*
* @param index index of the element to return
* @return the element at the specified position in this list
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
get: function (index) {
checkElementIndex(index);
return node(index).item;
},
/**
* Replaces the element at the specified position in this list with the
* specified element.
*
* @param index index of the element to replace
* @param element element to be stored at the specified position
* @return the element previously at the specified position
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
set: function (index, element) {
checkElementIndex(index);
let x = getNode(index);
let oldVal = x.item;
x.item = element;
return oldVal;
},
/**
* Inserts the specified element at the specified position in this list.
* Shifts the element currently at that position (if any) and any
* subsequent elements to the right (adds one to their indices).
*
* @param index index at which the specified element is to be inserted
* @param element element to be inserted
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
addIn: function (index, element) {
checkPositionIndex(index);
if (index === size)
linkLast(element);
else
linkBefore(element, getNode(index));
},
/**
* Removes the element at the specified position in this list. Shifts any
* subsequent elements to the left (subtracts one from their indices).
* Returns the element that was removed from the list.
*
* @param index the index of the element to be removed
* @return the element previously at the specified position
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
removeIn: function (index) {
checkElementIndex(index);
return unlink(getNode(index));
},
// Queue operations.
/**
* Retrieves, but does not remove, the head (first element) of this list.
*
* @return the head of this list, or {@code null} if this list is empty
*/
peek: function () {
const f = first;
return (f == null) ? null : f.item;
},
/**
* Retrieves, but does not remove, the head (first element) of this list.
*
* @return the head of this list
* @throws NoSuchElementException if this list is empty
*/
element: function () {
return getFirst();
},
/**
* Retrieves and removes the head (first element) of this list.
*
* @return the head of this list, or {@code null} if this list is empty
*/
poll: function () {
const f = first;
return (f == null) ? null : unlinkFirst(f);
},
/**
* Adds the specified element as the tail (last element) of this list.
*
* @param e the element to add
*/
offer: function (e) {
return this.add(e);
},
// Deque operations
/**
* Inserts the specified element at the front of this list.
*
* @param e the element to insert
*/
offerFirst: function (e) {
this.addFirst(e);
return true;
},
/**
* Inserts the specified element at the end of this list.
*
* @param e the element to insert
*/
offerLast: function (e) {
this.addLast(e);
return true;
},
/**
* Retrieves, but does not remove, the first element of this list,
* or returns {@code null} if this list is empty.
*
* @return the first element of this list, or {@code null}
* if this list is empty
* @since 1.6
*/
peekFirst: function () {
const f = first;
return (f == null) ? null : f.item;
},
/**
* Retrieves, but does not remove, the last element of this list,
* or returns {@code null} if this list is empty.
*
* @return the last element of this list, or {@code null}
* if this list is empty
* @since 1.6
*/
peekLast: function () {
const l = last;
return (l == null) ? null : l.item;
},
/**
* Retrieves and removes the first element of this list,
* or returns {@code null} if this list is empty.
*
* @return the first element of this list, or {@code null} if
* this list is empty
* @since 1.6
*/
pollFirst: function () {
const f = first;
return (f == null) ? null : unlinkFirst(f);
},
/**
* Retrieves and removes the last element of this list,
* or returns {@code null} if this list is empty.
*
* @return the last element of this list, or {@code null} if
* this list is empty
* @since 1.6
*/
pollLast: function () {
const l = last;
return (l == null) ? null : unlinkLast(l);
},
/**
* Pushes an element onto the stack represented by this list. In other
* words, inserts the element at the front of this list.
*
* <p>This method is equivalent to {@link #addFirst}.
*
* @param e the element to push
* @since 1.6
*/
push: function (e) {
this.addFirst(e);
},
/**
* Pops an element from the stack represented by this list. In other
* words, removes and returns the first element of this list.
*
* <p>This method is equivalent to {@link #removeFirst()}.
*
* @return the element at the front of this list (which is the top
* of the stack represented by this list)
* @throws NoSuchElementException if this list is empty
* @since 1.6
*/
pop: function () {
return this.removeFirst();
},
/**
* Removes the first occurrence of the specified element in this
* list (when traversing the list from head to tail). If the list
* does not contain the element, it is unchanged.
*
* @param o element to be removed from this list, if present
*/
removeFirstOccurrence: function (o) {
return remove(o);
},
/**
* Removes the last occurrence of the specified element in this
* list (when traversing the list from head to tail). If the list
* does not contain the element, it is unchanged.
*
* @param o element to be removed from this list, if present
*/
removeLastOccurrence: function (o) {
if (o === null) {
for (let x = last; x != null; x = x.prev) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (let x = last; x != null; x = x.prev) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
},
/**
* Returns the index of the first occurrence of the specified element
* in this list, or -1 if this list does not contain the element.
* More formally, returns the lowest index {@code i} such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>,
* or -1 if there is no such index.
*
* @param o element to search for
* @return the index of the first occurrence of the specified element in
* this list, or -1 if this list does not contain the element
*/
indexOf: function (o) {
let index = 0;
if (o == null) {
for (let x = first; x != null; x = x.next) {
if (x.item == null)
return index;
index++;
}
} else {
for (let x = first; x != null; x = x.next) {
if (o === x.item)
return index;
index++;
}
}
return -1;
},
/**
* Returns the index of the last occurrence of the specified element
* in this list, or -1 if this list does not contain the element.
* More formally, returns the highest index {@code i} such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>,
* or -1 if there is no such index.
*
* @param o element to search for
* @return the index of the last occurrence of the specified element in
* this list, or -1 if this list does not contain the element
*/
lastIndexOf: function (o) {
let index = size;
if (o == null) {
for (let x = last; x != null; x = x.prev) {
index--;
if (x.item == null)
return index;
}
} else {
for (let x = last; x != null; x = x.prev) {
index--;
if (o.equals(x.item))
return index;
}
}
return -1;
},
/**
* Removes all of the elements from this list.
* The list will be empty after this call returns.
*/
clear: function () {
for (let x = first; x != null;) {
let next = x.next;
x.item = null;
x.next = null;
x.prev = null;
x = next;
}
first = last = null;
size = 0;
},
// 遍历链表
forEach: function (call) {
let idx = 0;
let node = first;
while (node.next !== null) {
call(node, idx);
node = node.next;
idx++;
}
},
// 映射成数组
map: function (call) {
let idx = 0;
let node = first;
let ret = [];
while (node.next !== null) {
let res = call(node, idx);
if (res !== undefined) {
ret.push(res);
}
node = node.next;
idx++;
}
return ret;
},
//游标时候有下一个元素
hasNextItem: function () {
return cursor.next !== null;
},
//游标是否有前一个元素
hasPrevItem: function () {
return cursor.prev !== null;
},
//游标当前指向的值
currentValue: function () {
return cursor.item;
},
//游标指向第一个元素
firstItem: function () {
cursor = first;
return cursor.item;
},
//游标指向最后一个元素
lastItem: function () {
cursor = last;
return cursor.item;
},
//游标指向后一个元素
nextItem: function () {
if (cursor.next !== null) {
cursor = cursor.next;
return cursor.item;
}
},
//游标指向前一个元素
prevItem: function () {
if (cursor.pre !== null) {
cursor = cursor.pre;
return cursor.item;
}
}
}
}
export default LinkedList;
疯狂的妞妞 :每一天,做什么都好,不要什么都不做!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY