书接上回,上一篇文章讲了单向链表以及用 js 封装一个单向链表,所以这节将介绍双向链表以及用 js 封装一个双向链表。待会我也会继续在文章后面附上视频学习链接地址,大家想学习的可以去看看
一、认识双向链表
首先来认识一下什么是双向链表,以及单向链表与双向链表的区别和它们各自的缺点
双向链表的图解
二、双向链表的常见操作
三、双向链表的封装
1、append 方法
// 封装 append 追加方法
DoublyLinkedList.prototype.append = function() {
// 1、根据data创建节点
var newNode = new Node(data)
// 2、判断添加的是否是第一个节点
if (this.length == 0) {
this.head = newNode
this.tail = newNode
} else {
newNode.prev = this.tail
this.tail.next = newNode
this.tail = newNode
}
// 3、length + 1
this.length += 1
}
2、toString 方法
// 2、封装将链表转成字符串形式的方法
// 2.1 toString 方法
DoublyLinkedList.prototype.toString = function() {
return this.backwordString()
}
// 2.2 forwordString 方法
DoublyLinkedList.prototype.forwordString = function() {
// 定义变量
var current = this.tail
var resultString = ""
// 依次向前遍历,获取每一个节点
while (current) {
resultString += current.data + " "
current = current.prev
}
// 返回最终的值
return resultString
}
// 2.3 backwordString 方法
DoublyLinkedList.prototype.backwordString = function() {
// 定义变量
var current = this.head
var resultString = ""
// 依次向后遍历,获取每一个节点
while (current) {
resultString += current.data + " "
current = current.next
}
// 返回最终的值
return resultString
}
3、insert 方法
// 3、封装 insert 插入方法
DoublyLinkedList.prototype.insert = function(position, data) {
// 越界判断
if (position < 0 || position > this.length) return false
// 根据 data 创建新的节点
var newNode = new Node(data)
// 判断原来的列表是否为空
if (this.length == 0) {
this.head = newNode
this.tail = newNode
} else {
if (position == 0) { // 如果插入到开头
this.head.prev = newNode // 将原来节点的前一个节点指向新得节点
newNode.next = this.head // 将新节点的下一个节点指向原来的节点
this.head = newNode // 将头结点指向新插入的节点
} else if (position == this.length) { // 如果插入到结尾
newNode.prev = this.tail // 将新插入节点的前一个节点指向原来的最后一个节点
this.tail.next = newNode // 将原来最后一个节点的下一个节点指向新插入的节点
this.tail = newNode // 将尾节点指向新插入的节点
} else { // 如果插入到中间
var current = this.head
var index = 0
while (index++ < position) {
current = current.next
}
// 修改指针
newNode.next = current // 将新插入节点的下一个节点指向原来位置上的节点
newNode.prev = current.prev // 将新插入的节点的上一个节点指向原来位置上节点的上一个节点
current.prev.next = newNode // 将原来位置上节点的上一个节点的下一个节点指向新的节点
current.prev = newNode // 将原来位置上节点的上一个节点指向新的节点
}
}
// length + 1
this.length += 1
return true
}
图解中间插入时的情况
4、get 方法
// 方法一 : 从前往后查找,效率低下
// 4、封装 get 获取方法
DoublyLinkedList.prototype.get = function(position) {
// 越界判断
if (position < 0 || position >= this.length) return null
// 获取对应的data
var current = this.head // 定义当前值为头节点
var index = 0 // 定义索引值为0
while (index++ < position) { // 循环找到需要的 position 对应位置的值
current = current.next
}
return current.data
}
// 方法二 : 与长度的一般进行比较,小于的时候从前往后找,大于的时候从后往前找,效率比较高
// 4、封装 get 获取方法
DoublyLinkedList.prototype.get = function(position) {
// 越界判断
if (position < 0 || position >= this.length) return null
if (position <= this.length / 2) { // 如果要查找的位置小于长度的一半
// 获取对应的data
var current = this.head // 定义当前值为头节点
var index = 0 // 定义索引值为0
while (index++ < position) { // 循环找到需要的 position 对应位置的值
current = current.next // 让current指向他的后一个节点
}
return current.data
} else if (position > this.length / 2) { // 如果要查找的位置大于长度的一半
var current = this.tail // 定义当前值为尾节点
var index = this.length - 1 // 定义索引值为长度减一
while (index-- > position) { // 循环找到需要的 position 对应位置的值
current = current.prev // 让current指向他的前一个节点
}
return current.data
}
}
5、indexOf 方法
// 5、封装 indexOf 返回指定数据的下标值方法
DoublyLinkedList.prototype.indexOf = function(data) {
// 定义变量
var current = this.head
var index = 0
// 查找current的data与传入值的data是否相等
while (current) {
if (current.data == data) {
return index
}
index += 1
current = current.next
}
return -1
}
6、update 方法
// 方法一 : 从前往后查找,效率低下
// 6、封装 update 更新指定位置数据的方法
DoublyLinkedList.prototype.update = function(position, newData) {
// 越界判断
if (position < 0 || position > this.length) return false
// 定义变量
var current = this.head
var index = 0
while (index++ < position) {
current = current.next
}
current.data = newData
return true
}
// 方法二 : 与长度的一般进行比较,小于的时候从前往后找,大于的时候从后往前找,效率比较高
// 6、封装 update 更新指定位置数据的方法
DoublyLinkedList.prototype.update = function(position, newData) {
// 越界判断
if (position < 0 || position > this.length) return false
if (position <= this.length / 2) {
// 定义变量
var current = this.head
var index = 0
while (index++ < position) {
current = current.next
}
current.data = newData
return true
} else if (position > this.length / 2) {
// 定义变量
var current = this.tail
var index = this.length - 1
while (index-- > position) {
current = current.prev
}
current.data = newData
return true
}
}
7、removeAt 方法
// 7、封装 removeAt 删除指定位置的节点方法
DoublyLinkedList.prototype.removeAt = function(position) {
// 越界判断
if (position < 0 || position > this.length) return null
var current = this.head
// 判断是否只有一个节点
if (this.length == 1) {
this.head = null
this.tail = null
} else {
if (position == 0) { // 判断删除的是否是第一个节点
this.head.next.prev = null
this.head = this.head.next
} else if (position == this.length - 1) { // 判断删除的是否是最后一个节点
current = this.tail
this.tail.prev.next = null
this.tail = this.tail.prev
} else { // 删除的是其他的节点
var index = 0
while (index++ < position) {
current = current.next
}
current.prev.next = current.next
current.next.prev = current.prev
}
}
// 长度减一
this.length -= 1
return current.data
}
8、remove 方法
// 8、封装 remove 删除指定数据的节点的方法
DoublyLinkedList.prototype.remove = function(data) {
// 1、根据data获取下标值
var index = this.indexOf(data)
// 2、根据index删除对应位置的节点
return this.removeAt(index)
}
9、isEmpty 方法
// 9、封装 isEmpty 判断链表是否为空方法
DoublyLinkedList.prototype.isEmpty = function() {
return this.length == 0
}
10、size 方法
// 10、封装 size 返回链表长度方法
DoublyLinkedList.prototype.size = function() {
return this.length
}
11、getHead 方法
// 11、获取链表的第一个元素
DoublyLinkedList.prototype.getHead = function() {
return this.head.data
}
12、getTail 方法
// 12、获取链表的最后一个元素
DoublyLinkedList.prototype.getTail = function() {
return this.tail.data
}
完整代码加测试代码
// 封装链表类
function DoublyLinkedList() {
// 内部类:节点类
function Node(data) {
this.data = data
this.prev = null
this.next = null
}
// 属性
this.head = null
this.tail = null
this.length = 0
// 封装常见的操作
// 封装 append 追加方法
DoublyLinkedList.prototype.append = function(data) {
// 1、根据data创建节点
var newNode = new Node(data)
// 2、判断添加的是否是第一个节点
if (this.length == 0) {
this.head = newNode
this.tail = newNode
} else {
newNode.prev = this.tail
this.tail.next = newNode
this.tail = newNode
}
// 3、length + 1
this.length += 1
}
// 2、封装将链表转成字符串形式的方法
// 2.1 toString 方法
DoublyLinkedList.prototype.toString = function() {
return this.backwordString()
}
// 2.2 forwordString 方法
DoublyLinkedList.prototype.forwordString = function() {
// 定义变量
var current = this.tail
var resultString = ""
// 依次向前遍历,获取每一个节点
while (current) {
resultString += current.data + " "
current = current.prev
}
// 返回最终的值
return resultString
}
// 2.3 backwordString 方法
DoublyLinkedList.prototype.backwordString = function() {
// 定义变量
var current = this.head
var resultString = ""
// 依次向后遍历,获取每一个节点
while (current) {
resultString += current.data + " "
current = current.next
}
// 返回最终的值
return resultString
}
// 3、封装 insert 插入方法
DoublyLinkedList.prototype.insert = function(position, data) {
// 越界判断
if (position < 0 || position > this.length) return false
// 根据 data 创建新的节点
var newNode = new Node(data)
// 判断原来的列表是否为空
if (this.length == 0) {
this.head = newNode
this.tail = newNode
} else {
if (position == 0) { // 插入到开头
this.head.prev = newNode
newNode.next = this.head
this.head = newNode
} else if (position == this.length) { // 插入到结尾
newNode.prev = this.tail
this.tail.next = newNode
this.tail = newNode
} else { // 插入到中间
var current = this.head
var index = 0
while (index++ < position) {
current = current.next
}
// 修改指针
newNode.next = current
newNode.prev = current.prev
current.prev.next = newNode
current.prev = newNode
}
}
// length + 1
this.length += 1
return true
}
// 4、封装 get 获取方法
DoublyLinkedList.prototype.get = function(position) {
// 越界判断
if (position < 0 || position >= this.length) return null
if (position <= this.length / 2) {
// 获取对应的data
var current = this.head // 定义当前值为头节点
var index = 0 // 定义索引值为0
while (index++ < position) { // 循环找到需要的 position 对应位置的值
current = current.next
}
return current.data
} else if (position > this.length / 2) {
var current = this.tail
var index = this.length - 1
while (index-- > position) {
current = current.prev
}
return current.data
}
}
// 5、封装 indexOf 返回指定数据的下标值方法
DoublyLinkedList.prototype.indexOf = function(data) {
// 定义变量
var current = this.head
var index = 0
// 查找current的data与传入值的data是否相等
while (current) {
if (current.data == data) {
return index
}
index += 1
current = current.next
}
return -1
}
// 6、封装 update 更新指定位置数据的方法
DoublyLinkedList.prototype.update = function(position, newData) {
// 越界判断
if (position < 0 || position > this.length) return false
if (position <= this.length / 2) {
// 定义变量
var current = this.head
var index = 0
while (index++ < position) {
current = current.next
}
current.data = newData
return true
} else if (position > this.length / 2) {
// 定义变量
var current = this.tail
var index = this.length - 1
while (index-- > position) {
current = current.prev
}
current.data = newData
return true
}
}
// 7、封装 removeAt 删除指定位置的节点方法
DoublyLinkedList.prototype.removeAt = function(position) {
// 越界判断
if (position < 0 || position > this.length) return null
var current = this.head
// 判断是否只有一个节点
if (this.length == 1) {
this.head = null
this.tail = null
} else {
if (position == 0) { // 判断删除的是否是第一个节点
this.head.next.prev = null
this.head = this.head.next
} else if (position == this.length - 1) { // 判断删除的是否是最后一个节点
current = this.tail
this.tail.prev.next = null
this.tail = this.tail.prev
} else { // 删除的是其他的节点
var index = 0
while (index++ < position) {
current = current.next
}
current.prev.next = current.next
current.next.prev = current.prev
}
}
// 长度减一
this.length -= 1
return current.data
}
// 8、封装 remove 删除指定数据的节点的方法
DoublyLinkedList.prototype.remove = function(data) {
// 1、根据data获取下标值
var index = this.indexOf(data)
// 2、根据index删除对应位置的节点
return this.removeAt(index)
}
// 9、封装 isEmpty 判断链表是否为空方法
DoublyLinkedList.prototype.isEmpty = function() {
return this.length == 0
}
// 10、封装 size 返回链表长度方法
DoublyLinkedList.prototype.size = function() {
return this.length
}
// 11、获取链表的第一个元素
DoublyLinkedList.prototype.getHead = function() {
return this.head.data
}
// 12、获取链表的最后一个元素
DoublyLinkedList.prototype.getTail = function() {
return this.tail.data
}
}
// 测试代码
// 1、创建LinkedList
var list = new DoublyLinkedList()
// 2、测试append方法
list.append('abc')
list.append('tng')
list.append('ccc')
console.log(list.toString());
console.log(list.forwordString());
console.log(list.backwordString());
// 测试insert方法
list.insert(0, 'aaa')
list.insert(4, 'ddd')
list.insert(2, 'bbb')
console.log(list.toString());
// 测试get方法
console.log(list.get(0));
console.log(list.get(2));
console.log(list.get(5));
console.log(list.get(1));
// 测试indexOf方法
console.log(list.indexOf('aaa'));
console.log(list.indexOf('bbb'));
console.log(list.indexOf('ddd'));
console.log(list.indexOf('ccc'));
// 测试update方法
list.update(4, 'cc')
console.log(list.toString());
// 测试removeAt方法
list.removeAt(1)
console.log(list.toString());
// 测试remove方法
list.remove('ddd')
console.log(list.toString());
// 测试其他方法
console.log(list.isEmpty());
console.log(list.size());
console.log(list.getHead());
console.log(list.getTail());
下面附上b站视频链接,需要学习的可以去看看(JavaScript算法与数据结构)