链表是一种线性结构,由一个一个的节点连接而成,具体结构如下:
可以看出,每个节点保存了两个值,一个值是节点自身的数据,另一个值是一个指针,指向下一个节点。通过next指针就可以将所有节点连接起来。节点的数据结构如下:
class LinkNode {constructor (e = null, next = null) {this.e = ethis.next = next}toString () { // 方便打印return this.e.toString()}}
根据上述说明,可以写出链表的数据结构:
class LinkedList {constructor () {this.dummyHead = new LinkNode() // 为了方便操作使用了一个虚拟头结点,这个节点的next指针指向头结点this.size = 0}getSize () {return this.size}isEmpty () {return this.size === 0}add (index, e) {if (index < 0 && index >= this.size) throw new RangeError('Add failed. Inlegal index')let prev = this.dummyHeadfor (let i = 0; i < index; i ++) {prev = prev.next}// const Node = new LinkNode(e)// Node.next = prev.next// prev.next = Node// 上面三行代码等价于下面这句prev.next = new LinkNode(e, prev.next)this.size ++}addFirst (e) {this.add(0, e)}addLast (e) {this.add(this.size, e)}get (index) {if (index < 0 || index >= this.size) throw new RangeError('Add failed. Inlegal index')let cur = this.dummyHead.nextfor (let i = 0; i < index; i ++) {cur = cur.next}return cur.e}getFirst () {return this.get(0)}getLast () {return this.get(this.size - 1)}set (index, e) {if (index < 0 || index >= this.size) throw new RangeError('Add failed. Inlegal index')let cur = this.dummyHead.nextfor (let i = 0; i < index; i ++) {cur = cur.next}cur.e = e}contains (e) {let cur = this.dummyHead.nextwhile (cur !== null) {if (cur.e === e) return truecur = cur.next}return false}remove (index) {let prev = this.dummyHeadfor (let i = 0; i < index; i ++) {prev = prev.next}const node = prev.nextprev.next = prev.next.nextnode.next = nullthis.size --}removeFirst () {this.remove(0)}removeLast () {this.remove(this.size - 1)}removeElement (e) {let cur = this.dummyHeadwhile (cur.next) {if (cur.next.e === e) cur.next = cur.next.nextelse cur = cur.next}}toString () {let ret = ''let cur = this.dummyHead.nextfor (; cur !== null; cur = cur.next) {ret += cur.toString() + ' => '}ret += 'NULL'return ret}}module.exports = LinkedList
也可以使用递归的方式:
/*** 使用递归的方式*/class LinkedList {constructor () {this.head = nullthis.size = 0}isEmpty () {return this.size === 0}getSize () {return this.size}get (index) {if (index < 0 || index > this.size) throw new RangeError('Get failed. Illegal index.')return this._get(index, this.head)}_get (index, node) {if (index === 0) return node.elreturn this._get(index - 1, node.next)}getFirst () {return this.get(0)}getLast () {return this.get(this.size)}set (index, el) {if (index < 0 || index >= this.size) throw new RangeError('Add failed. Illegal index.')this._set(index, el, this.head)}_set (index, el, node) {if (index === 0) {node.el = elreturn}this._set(index - 1, el, node.next)}setFirst (el) {this.set(0, el)}setLast (el) {this.set(this.size, el)}contains (el) {return this._contains(el, this.head)}_contains (el, node) {if (node === null) return falseif (node.el === el) return truereturn this._contains(el, node.next)}add (el, index) {if (index < 0 || index > this.size) throw new RangeError('Add failed. Illegal index.')this.head = this._add(this.head, el, index)this.size ++}_add (node, el, index) {if (index === 0) return new LinkNode(el, node)node.next = this._add(node.next, el, index - 1)return node}addFirst (el) {this.add(el, 0)}addLast (el) {this.add(el, this.size)}remove (index) {if (index < 0 || index >= this.size) throw new RangeError('Add failed. Illegal index.')const ret = this._remove(index, this.head)this.head = ret.keys().next().valuethis.size --return ret.get(this.head)}_remove (index, node) {// 这里注意,由于需要返回被删除的节点的值,所以使用map进行保存if (index === 0) return new Map([[node.next, node.el]])const ret = this._remove(index - 1, node.next)node.next = ret.keys().next().value // 利用迭代器的特性取出key值return new Map([[node, ret.values().next().value]])}removeFirst () {return this.remove(0)}removeLast () {return this.remove(this.size - 1)}removeElement (el) {this.head = this._removeElement(el, this.head)}_removeElement (el, node) {if (node === null) return nullnode.next = this._removeElement(el, node.next) // 注意要先执行这一步求出移除el后子链表的头结点if (node.el === el) {this.size --return node.next}return node}toString () {let ret = '[LinkedList]: 'let cur = this.headwhile (cur !== null) {ret += cur.toString() + ' => 'cur = cur.next}ret += 'NULL'return ret}}module.exports = LinkedList
需要注意的是,递归的方式是将链表分为头结点和子链表,以add为例,_add内部将链表拆分后,子链表的头结点即是head.next,而相对应的第index个节点子链表中则是第index - 1个节点。
使用链表也可以实现栈和队列,且因为链表是动态的线性结构,因此不需要考虑内存的问题:
实现栈:
const LinkedList = require('./LinkedList')class Stack {constructor () {this.data = new LinkedList()}push (e) {this.data.addFirst(e)}pop () {return this.data.removeFirst()}peek () {return this.data.getFirst()}toString () {return `[Stack]: top ${this.data.toString()}`}}module.exports = Stack
实现队列:
class LinkNode {constructor (e = null, next = null) {this.e = ethis.next = next}toString () {return this.e.toString()}}class Queue {constructor () {this.head = nullthis.tail = nullthis.size = 0}isEmpty () {return this.size === 0}enqueue (e) {if (this.size === 0) {this.head = this.tail = new LinkNode(e)} else {this.tail.next = new LinkNode(e)this.tail = this.tail.next}this.size ++}dequeue () {if (this.size === 0) throw new RangeError('cannot dequeue element from empty queue.')const ret = this.headthis.head = this.head.nextif (this.head === null) this.tail = nullthis.size --return ret}getFront () {return this.head}toString () {let ret = '[Queue]: front 'let cur = this.headfor (; cur !== null; cur = cur.next) {ret += cur.toString() + ' => '}ret += 'NULL tail'return ret}}module.exports = Queue
