节点结构
保存 element 节点元素和 next 指针即可:
function Node(element, next) {
this.element = element // 节点元素
this.next = next // 下一个节点指针
}
链表结构
这里保存了头尾指针和节点个数,其中保存尾指针的目的是快速在尾部添加元素,否则要从头开始遍历。
function LinkedList() {
this.head = null // 头指针
this.tail = null // 尾指针
this.size = 0 // 节点个数
}
末尾添加元素
如果末尾元素不存在,表明是空链表,否则就添加进去,并把 head 和 tail 都设置成该元素。
LinkedList.prototype.push = function (element) {
const node = new Node(element, null)
if (this.tail === null) {
this.head = this.tail = node
} else {
this.tail.next = node
this.tail = node
}
return ++this.size
}
弹出末尾元素
如果链表长度为1,则需要置空链表,把 head 和 tail 都设置成 null,否则就遍历找到倒数第二个节点,设置其为 tail 并把它的 next 置空即可。
LinkedList.prototype.pop = function (element) {
if (this.tail === null) return null
let last
if (this.size === 1) {
last = this.tail
this.head = null
this.tail = null
} else {
let i = 0, prev = this.head
while (i++ < this.size - 1) prev = prev.next
last = prev.next
prev.next = null
this.tail = prev
}
this.size--
return last
}
插入元素
先判断边界,只有在 [0, this.size] 之间的位置允许插入,先找到当前位置前面的那个元素 prev,将新元素的 next 设置为 prev.next,然后再将 prev.next 设置为该元素即可。同样要注意如果在头部插入需要额外处理 head。
LinkedList.prototype.insert = function (index, element) {
if (index < 0 || index > this.size) throw new Error('索引越界')
if (index === 0) {
this.head = new Node(element, this.head)
if (this.size === 0) this.tail = this.head
} else {
let i = 0, prev = this.head
while (i++ < index - 1) prev = prev.next
prev.next = new Node(element, prev.next)
if (this.size === index) this.tail = prev.next
}
this.size++
}
删除元素
逻辑跟插入类似,都要找到指定位置之前的那个元素。这里要注意,删除头部和尾部元素时都需要特殊处理。
LinkedList.prototype.remove = function (index) {
if (index < 0 || index >= this.size) throw new Error('索引越界')
let current
if (index === 0) {
current = this.head
this.head = current.next
if (this.size === 1) this.tail = null
} else {
let i = 0, prev = this.head
while (i++ < index - 1) prev = prev.next
current = prev.next
prev.next = current.next
if (this.size === index - 1) this.tail = prev
}
current.next = null
this.size--
return current
}
获取元素
这个最简单,直接遍历即可。
LinkedList.prototype.get = function (index) {
if (index < 0 || index >= this.size) throw new Error('索引越界')
let i = 0, current = this.head
while (i++ < index) current = current.next
return current
}
反转链表
把链表进行反转,即把 tail 变 head,head 变 tail,各 node 的 next 指向反过来。下面给出三种反转思路:
-
循环遍历,两两反转
LinkedList.prototype.reverse = function () { if (this.size <= 1) return this let current = this.head, next = current.next while (next) { const third = next.next next.next = current current = next next = third } this.head.next = null this.tail = this.head this.head = current return this } -
递归遍历反转
LinkedList.prototype.reverse = function () { const rev = (head) => { const next = head.next if (head === null || next === null) return head const newHead = rev(next) next.next = head head.next = null return newHead } const newHead = rev(this.head) this.tail = this.head this.head = newHead return this } -
循环遍历,逐个反转
LinkedList.prototype.reverse = function () { let head = this.head, newHead = null while (head !== null) { let tmp = head.next head.next = newHead newHead = head head = tmp } this.tail = this.head this.head = newHead return this }