「这是我参与11月更文挑战的第5天,活动详情查看:2021最后一次更文挑战
对比单向链表和双向链表
单向链表
只能从头遍历到尾 或者从尾遍历到头(一般都是从头到尾)
-
也就是说这个链表相连的过程是单向的
-
实现的原理是上一个链表中有一个指向下一个的引用
单向链表的缺点
- 可以轻松到达下一个节点,但很难回到上一个节点,只能从头开始,再一次遍历一遍
双向链表
-
既可以从头遍历到尾,又可以从尾遍历到头
-
实现原理:一个节点既有向后的引用,又有向前的引用
-
双向链表可以有效解决单向链表中的问题
双向链表的缺点
- 每次在插入或者删除某个节点时,需要处理四个引用,实现起来更困难
- 相对于单向链表,占的内存空间更大
双向链表的特点
-
可以使用一个 head 和一个 tail 分别指向头部和尾部的节点
-
每个节点都由三部分组成:前一个节点的指针(prev),保存的数据(data),下一个节点的指针(next)
-
双向链表的第一个节点的 prev 是 null
-
双向链表的最后的节点的 next 是 null
双向链表的常见操作
-
append(element):向列表尾部添加一个新的项
-
insert(position, element):向列表特定位置插入一个新的项
-
get(position):获取对应位置的元素
-
indexOf(element):返回元素在列表中的索引,如果没有,就返回 -1
-
update(position, element):修改某个位置的元素
-
removeAt(position):从列表的特定位置删除一项
-
remove(element):从列表中移除一项
-
isEmpty():判断链表是否为空
-
size():返回链表中的元素个数
-
forwardString():返回从后向前遍历的节点字符串形式
-
backwardString():返回从前向后遍历的节点字符串形式
-
toString()
-
getHead():返回链表的第一项
-
getTail:返回链表的最后一项
双向链表的代码实现
代码
class DoublyLinkedListItem {
data = undefined;
prev = null
next = null;
constructor(data) {
this.data = data;
}
}
class DoublyLinkedList {
head = null
tail = null
length = 0
append(data) {
const newItem = new DoublyLinkedListItem(data);
if(this.length === 0) {
this.head = newItem
this.tail = newItem
} else {
newItem.prev = this.tail
this.tail.next = newItem
this.tail = newItem
}
this.length += 1
}
insert(position, data) {
if(position < 0 || position > this.length) {return }
const newItem = new DoublyLinkedListItem(data);
if (this.length === 0) {
this.head = newItem
this.tail = newItem
} else {
if (position === 0) {
newItem.next = this.head
this.head.prev = newItem
this.head = newItem
} else if (position === this.length) {
newItem.prev = this.tail
this.tail.next = newItem
this.tail = newItem
} else {
let currentItem = this.head
for(let i = 0;i < position; i ++) {
currentItem = currentItem.next
}
newItem.prev = currentItem.prev
newItem.next = currentItem
currentItem.prev.next = newItem
currentItem.prev = newItem
}
}
this.length += 1
}
get(position) {
if(position < 0 || position >= this.length) {return null}
let currentItem = this.head
for(let i = 0; i < position; i++) {
currentItem = currentItem.next
}
return currentItem.data
}
indexOf(data) {
let currentItem = this.head
for (let i = 0;i < this.length; i++) {
if(currentItem.data === data) {
return i
} else {
currentItem = currentItem.next
if (currentItem === null) {
return -1
}
}
}
}
update(position, newData) {
if(position < 0 || position >= this.length) {return null}
let currentItem = this.head
for(let i = 0; i < position; i++) {
currentItem = currentItem.next
}
currentItem.data = newData
}
removeAt(position) {
if(position < 0 || position >= this.length) {return }
let currentItem = 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) {
currentItem = this.tail
this.tail.prev.next = null
this.tail = this.tail.prev
} else {
for (let i = 0; i < position; i ++) {
currentItem = currentItem.next
}
currentItem.prev.next = currentItem.next
currentItem.next.prev = currentItem.prev
}
}
this.length -= 1
return currentItem.data
}
remove(data) {
const index = this.indexOf(data)
return this.removeAt(index)
}
toString() {
return this.backwardString()
}
forwardString() {
let currentItem = this.tail
let resultString = ''
for(let i = 0; i < this.length; i ++ ){
resultString += currentItem.data + " "
currentItem = currentItem.prev
}
return resultString
}
backwardString() {
let currentItem = this.head
let resultString = ''
for(let i = 0; i < this.length; i ++ ){
resultString += currentItem.data + " "
currentItem = currentItem.next
}
return resultString
}
isEmpty() {
return this.length === 0
}
size() {
return this.length
}
getHead() {
return this.head.data
}
getTail() {
return this.tail.data
}
}
测试代码
const doublyLinkedList = new DoublyLinkedList()
doublyLinkedList.append('abc');
doublyLinkedList.append('cba');
doublyLinkedList.append('nba');
doublyLinkedList.append('ban');
console.log(doublyLinkedList.backwardString()); //abc cba nba ban
console.log(doublyLinkedList.forwardString()); //ban nba cba abc
console.log(doublyLinkedList.toString()); //abc cba nba ban
console.log(doublyLinkedList.size()) //4
console.log(doublyLinkedList.isEmpty()) //false
doublyLinkedList.update(3, 'bbb')
console.log(doublyLinkedList.removeAt(2)); //'nba'
console.log(doublyLinkedList.remove('nba')) //undefined
console.log(doublyLinkedList.toString()); //abc cba bbb
doublyLinkedList.insert(0, 'aaa')
doublyLinkedList.insert(5, 'ccc')
doublyLinkedList.insert(2, 'bbb')
console.log(doublyLinkedList.toString()); //aaa abc bbb cba bbb
console.log(doublyLinkedList.get(3)); // cba
console.log(doublyLinkedList.indexOf('abc')) // 1
console.log(doublyLinkedList.getHead()) //aaa
console.log(doublyLinkedList.getTail()) //bbb