双向链表的结点
与单链表不同的是多了prev指针,如图所示:
封装双向链表的结点
JavaScipt没有直接提供该数据结构,需要自己封装
class DoublyNode{
constructor(data){
this.prev = null
this.data = data
this.next = null
}
}
export default DoublyNode
什么是双向链表
双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。
如图所示:
双向链表的特点
结点除了存储数据外,还有两个指针分别指向前一个结点地址(前驱指针prev)和下一个结点地址(后继指针next)。
封装双向链表
包含链尾添加结点,指定位置插入数据,找到指定位置的结点,删除指定位置的结点,查找链头结点,查找链尾结点,清空链表,删除指定的数据,返回指定数据的索引,转换为字符串,返回链表长度,判断链表是否为空等方法。
import DoublyNode from './DoublyNode.mjs'
class DoublyLinkedList {
//记录链表长度
#length = 0
//链表头指针-
#head = null
//链表尾指针
#tail = null
//在链尾添加数据结点
push(data) {
//创建好结点
const node = new DoublyNode(data)
//链表为空时
if (this.#head === null) {
//头尾指针都指向新插入的结点
this.#head = node
this.#tail = node
} else {
//链表此时不为空,直接在链尾添加数据
//将node连接在tail上
this.#tail.next = node
//node上的prev需要连接到之前的链尾结点
node.prev = this.#tail
//把node作为新的链尾
this.#tail = node
}
//结点增加,链表长度增加
this.#length++
}
//指定位置插入数据
insert(data, index) {
//判断index是否存在
if (index >= 0 && index <= this.#length) {
//创建node结点
const node = new DoublyNode(data)
//用于存放node,暂时指向head(中途插入需要用到)
let current = this.#head
//在链头插入
if (index === 0) {
//链表内没有任何结点
if (this.#head === null) {
this.#head = node
this.#tail = node
} else {
//链表中存在结点
//将node从链头插入
node.next = this.#head
//原来链头结点的prev要指向node
this.#head.prev = node
//将node作为新链头
this.#head = node
}
} else if (/*在链尾插入结点*/index === this.#length) {
//将node连接在链尾
this.#tail.next = node
//将node的prev连接到之前的链尾结点
node.prev = this.#tail
//把node作为新链尾
this.#tail = node
} else {
//在链表中插入
//先找到需要插入结点的上一结点
const previous = this.getNodeAt(index - 1)
//再找到需要插入结点的下一结点
current = previous.next
//插入node结点
//将node上的next指向current
node.next = current
//current的prev指向node
current.prev = node
//previous的next指向node
previous.next = node
//node的prev指向previous
node.prev = previous
}
//链表增长,长度增加
this.#length++
return true
}
return false
}
//找到指定位置的结点
getNodeAt(index) {
if (index >= 0 && index < this.#length) {
let node = this.#head
for (let i = 0; i < index; i++) {
node = node.next
}
return node
}
return
}
//删除指定位置的结点
removeAt(index) {
//先判断index是否存在
if (index >= 0 && index < this.#length) {
//保存需要删除的结点,暂时指向head
let current = this.#head
if (index === 0/*删除链头的结点*/) {
//将原有链头的下一结点作为新链头(current保护了需要删除的结点)
this.#head = current.next
if (this.size() === 0) {
return
}else if (this.#length === 1/*链表只有一个结点*/) {
//将tail指向null
this.#tail = null
} else/*此时的链表有多个结点*/ {
//由于head已经后移,此时只需要将新链头的prev赋空即可
this.#head.prev = null
}
} else if (index === this.#length - 1/*删除链尾的结点*/) {
//用临时变量保护即将被删除的结点
current = this.#tail
//将倒数第二个结点作为新的链尾
this.#tail = current.prev
//倒数第二个结点的next赋值为空
this.#tail.next = null
} else/*删除链表中的结点*/ {
//找到需要删除结点的上一结点
const previous = this.getNodeAt(index - 1)
//保护将被删除的结点
current = previous.next
//删除结点(跳过)
previous.next = current.next
//被删除结点的下一结点的prev需要跳过一个结点
current.next.prev = previous
}
this.#length--
return current.data
}
return
}
//查找链头结点
getHead() {
return this.#head
}
//查找链尾结点
getTail() {
return this.#tail
}
//清空链表
clear() {
this.#length = 0
this.#tail = null
this.#head = null
}
//删除指定的数据
remove(data) {
const index = this.indexOf(data)
return this.removeAt(index)
}
//返回指定数据的索引
indexOf(data) {
let current = this.#head
for (let i = 0; i < this.#length; i++) {
if (JSON.stringify(data) === JSON.stringify(current.data)) {
return i
}
current = current.next
}
return -1
}
//转换为字符串
toString() {
let string = ''
let current = this.#head
while (current) {
string += current.data + (current.next ? ',' : '')
current = current.next
}
return string
}
//返回链表长度
size() {
return this.#length
}
//判断链表是否为空
isEmpty() {
return this.size() === 0
}
}
export default DoublyLinkedList