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;
疯狂的妞妞 :每一天,做什么都好,不要什么都不做!