在JavaScript数据结构与算法——链表详解(上)中,我们探讨了一下链表的定义、实现原理以及单链表的实现。接下来我们进一步了解一下链表的其他内容。
1、双向链表
双向链表实现原理图:
与单向链表不同的是,图中增加了小线部分,每一个节点增加了一个指向前驱节点的属性,这样就实现了双向链表。
双向链表中,删除节点不需要知道待删除节点的前驱节点,只要把待删除节点指向前驱节点的属性指向NULL,同时把前驱节点指向待删除节点的后继节点即可,这样在删除节点时更高效。结合原理图比较好理解一点,双向链表中,删除节点原理如下图:
增加节点原理如下图:
2、JS实现双向链表
以下代码基于JavaScript数据结构与算法——链表详解(上)中的代码修改,文章末尾会附上完整代码。
从原理图可知,我们应该先给节点添加previous
属性,可通过修改Node
类实现:
2.1、Node类修改
// Created by xiaoqiang on 24/04/2018.
class Node {
constructor (element) {
this.element = element
this.next = null
this.previous = null
}
}
2.2、LinkedList类修改
class LinkedList {
constructor () {
this.header = new Node('header') // 头节点
}
// 查找元素
find (item) {
let currNode = this.header
while (currNode.element !== item) {
currNode = currNode.next
}
return currNode
}
// 用于返回最后一个节点
findLast () {
let currNode = this.header
while (currNode.next !== null) {
currNode = currNode.next
}
return currNode
}
// 插入节点
insert (item, newElement) {
let newNode = new Node(newElement)
let current = this.find(item)
newNode.next = current.next
newNode.previous = current
current.next = newNode
}
// 删除节点
remove (item) {
let currNode = this.find(item)
if (currNode.next !== null) {
currNode.previous.next = currNode.next
currNode.next.previous = currNode.previous
currNode.next = null
currNode.previous = null
}
}
// 显示链表
display () {
let currNode = this.header
while(currNode.next !== null) {
console.log(currNode.next.element)
currNode = currNode.next
}
}
// 反向显示链表
displayReverse () {
let currNode = this.header
currNode = this.findLast()
while (currNode.previous !== null) {
console.log(currNode.element)
currNode = currNode.previous
}
}
}
链表中的方法可根据需要添加。
2.3、测试实例修改:
let books = new LinkedList()
console.log('开始插入操作')
books.insert('header', 'JavaScript')
books.insert('JavaScript', 'PHP')
books.insert('PHP', 'Nodejs')
console.log('插入后的链表数据为:')
books.display()
console.log('开始进行移除操作')
books.remove('PHP')
console.log('移除后PHP后的链表的数据为:')
books.display()
console.log('反向显示链表:')
books.displayReverse()
2.3、双向链表完整代码
// Created by xiaoqiang on 24/04/2018.
class Node {
constructor (element) {
this.element = element
this.next = null
this.previous = null
}
}
class LinkedList {
constructor () {
this.header = new Node('header') // 头节点
}
// 查找元素
find (item) {
let currNode = this.header
while (currNode.element !== item) {
currNode = currNode.next
}
return currNode
}
// 用于返回最后一个节点
findLast () {
let currNode = this.header
while (currNode.next !== null) {
currNode = currNode.next
}
return currNode
}
// 插入节点
insert (item, newElement) {
let newNode = new Node(newElement)
let current = this.find(item)
newNode.next = current.next
newNode.previous = current
current.next = newNode
}
// 删除节点
remove (item) {
let currNode = this.find(item)
if (currNode.next !== null) {
currNode.previous.next = currNode.next
currNode.next.previous = currNode.previous
currNode.next = null
currNode.previous = null
}
}
// 显示链表
display () {
let currNode = this.header
while(currNode.next !== null) {
console.log(currNode.next.element)
currNode = currNode.next
}
}
// 反向显示链表
displayReverse () {
let currNode = this.header
currNode = this.findLast()
while (currNode.previous !== null) {
console.log(currNode.element)
currNode = currNode.previous
}
}
}
let books = new LinkedList()
console.log('开始插入操作')
books.insert('header', 'JavaScript')
books.insert('JavaScript', 'PHP')
books.insert('PHP', 'Nodejs')
console.log('插入后的链表数据为:')
books.display()
console.log('开始进行移除操作')
books.remove('PHP')
console.log('移除后PHP后的链表的数据为:')
books.display()
console.log('反向显示链表:')
books.displayReverse()
2.4、运行及结果
使用node运行此js文件,结果如下:
3、循环链表
循环链表实现原理如下图:
原理比双向链表更简单一些,有兴趣可以尝试实现一下,也可以给以上实现的链表类多添加一些方法,加深理解。
至此,JavaScript数据结构与算法——链表详解(下)完结,有错误欢迎指出。