JS数据结构(6)——双向链表
1.什么是双向链表?
单向链表
要知道什么是双向链表,我们首先得了解单向链表。
单向链表的详细解释在这里: JS单向链表
单向链表的特点:
- 只能从头遍历到尾
- 链表的连接过程是单向的
- 实现的原理是上一个链表结点中有一个指向下一个结点的next
单向链表的结构:
单向节点的缺点:
可以轻松到达下一个节点,但是回到前一个节点是非常困难的。
双向链表
双向链表的特点:
- 既可以从头遍历到尾,也可以从尾遍历到头
- 链表连接的过程时双向的
- 实现原理是一个链表结点,既有指向前一个的pre指针,也有指向后一个的next指针
- 双向链表可以有效的解决单向链表的缺点。
- 头节点用head表示,尾节点用tail表示
双向链表的缺点:
- 每次在插入或删除时,需要处理四个指针,实现起来困难‘
- 所占的内存空间比单向链表要大
双向链表的结构:
2.双向链表的封装
1.代码思路
- 我们封装一个DoublyLinkedList的类,用于表示双向链表的结构
- 在DoublLinkedList类中,需要封装一个内部类Node用于表示每个结点信息(包括指向上个结点的指针pre,指向下个结点的指针next,以及结点数据)
- 双向链表中我们还需要保存三个属性,链表的长度,链表的头结点head,以及链表的尾结点tail
- 双向链表的常见操作
(1)append(element):向链表尾部添加一个新的项
(2)insert(position,element):向链表的特定位置插入一个新的项
(3)get(position):获取对应位置的元素
(4)indexOf(element):返回元素在链表中的索引,如果没有返回-1
(5)update(position,element):修改某个位置的元素
(6)removeAt(position):移除链表中position位置的一项
(7)remove(element):移除链表中元素为element的一项
(8)isEmpty():判断链表是否为空,为空返回true,否则返回false
(9)size():返回链表中的元素个数
(10)forwardString():以字符串形式返回向前遍历的结点
(11)backwordString():以字符串形式返回向后遍历的结点
(12)getHead():获取链表的第一个元素
(13)getTail():获取链表的最后一个元素
2.代码实现
function DoublyLinkedList () {
this.head = null;
this.tail = null;
this.length = 0;
// 结点类
function Node(data) {
this.data = data;
this.prev = null;
this.next = null;
}
// append(element):向链表尾部添加一个新的项
DoublyLinkedList.prototype.append = function(element) {
var newNode = new Node(element);
if(this.length === 0) {
this.head = newNode;
this.tail = newNode;
}else {
newNode.prev = this.tail;
this.tail.next = newNode;
this.tail = newNode;
}
this.length ++;
}
// forwardString():以字符串形式返回向前遍历的结点
DoublyLinkedList.prototype.forwardString = function() {
var current = this.tail;
var resultString = "";
while(current){
resultString += current.data + " ";
current = current.prev;
}
return resultString;
}
// backwordString():以字符串形式返回向后遍历的结点
DoublyLinkedList.prototype.backwordString = function() {
var current = this.head;
var resultString = "";
while(current){
resultString += current.data + " ";
current = current.next;
}
return resultString;
}
// insert(position,element):向链表的特定位置插入一个新的项
DoublyLinkedList.prototype.insert = function(position, element) {
var current = this.head;
var index = 0;
var newNode = new Node(element);
if(position < 0 || position > this.length)
return null;
if(position == 0 ){
this.head.prev = newNode;
newNode.next = this.head;
this.head = newNode;
}else if(position == this.length){
this.tail.next = newNode;
newNode.prev = this.tail;
this.tail = newNode;
}else {
while(index++ < position - 1){
current = current.next;
}
newNode.next = current.next;
newNode.prev = current;
current.next.prev = newNode;
current.next = newNode;
}
this.length ++;
}
// get(position):获取对应位置的元素
DoublyLinkedList.prototype.get = function(position) {
if(position < 0 || position >= this.length)
return null;
var current = this.head;
var index = 0;
while(index < position) {
index ++;
current = current.next;
}
return current.data;
}
// indexOf(element):返回元素在链表中的索引,如果没有返回-1
DoublyLinkedList.prototype.indexOf = function(element) {
var current = this.head;
var index = 0;
while(current) {
if(current.data == element)
return index;
current = current.next;
index ++;
return index;
}
return -1;
}
// update(position,element):修改某个位置的元素
DoublyLinkedList.prototype.update = function(position,element) {
if(position < 0 || position >= this.length)
return null;
var index = 0;
var current = this.head;
while(index < position) {
index ++;
current = current.next;
}
current.data = element;
return true;
}
// removeAt(position):移除链表中position位置的一项
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) {
current = this.tail;
this.tail.prev.next = null;
this.tail = this.tail.prev;
}else {
var index = 0;
while(index < position) {
index ++;
current = current.next;
}
current.next.prev = current.prev;
current.prev.next = current.next;
}
}
this.length --;
return current.data;
}
// remove(element):移除链表中元素为element的一项
DoublyLinkedList.prototype.remove = function(element) {
var position = this.indexOf(element);
return this.removeAt(position);
}
// isEmpty():判断链表是否为空,为空返回true,否则返回false
DoublyLinkedList.prototype.isEmpty = function() {
return this.length == 0;
}
// size():返回链表中的元素个数
DoublyLinkedList.prototype.size = function() {
return this.length;
}
// getHead():获取链表的第一个元素
DoublyLinkedList.prototype.getHead = function() {
return this.head.data;
}
// getTail():获取链表的最后一个元素
DoublyLinkedList.prototype.getTail = function() {
return this.tail.data;
}
}
// 测试:
var DList = new DoublyLinkedList();
DList.append('aaa');
DList.append('bbb');
DList.append('ccc');
console.log(DList.backwordString()); // 结果为:aaa bbb ccc
console.log(DList.forwardString()); // 结果为:ccc bbb aaa
DList.insert(1,'hhh');
console.log(DList.backwordString()); // 结果为:aaa hhh bbb ccc
console.log(DList.get(2)); // 结果为:bbb
console.log(DList.indexOf('hhh')); // 结果为:1
console.log(DList.indexOf('abc')); // 结果为:-1
console.log(DList.update(2,'mmm')); // 结果为:true
console.log(DList.backwordString()); // 结果为:aaa hhh mmm ccc
console.log(DList.removeAt(2)); // 结果为:mmm
console.log(DList.backwordString());// 结果为:aaa hhh ccc
console.log(DList.remove('hhh')); // 结果为:hhh
console.log(DList.backwordString()); // 结果为:aaa ccc
console.log(DList.isEmpty()); // 结果为:false
console.log(DList.size()); // 结果为:2
console.log(DList.getHead()); // 结果为:aaa
console.log(DList.getTail()); // 结果为:ccc