一 .认识双向链表
单向链表:
之前说了单向链表,单向链表在插入和删除数据的时候效率会比数组快很多,但是在进行查找操作时效率就比较低,因为单向链表只能从头遍历到尾或者从尾遍历到头,也就是链表相连的过程是单向的。比如说当前的节点要到下一个节点是很容易的,但是要到上一个节点,就必须从头开始再次遍历。
为了解决单向链表只能从头遍历到尾的问题,就引出了双向链表。
双向链表:
双向链表相连的过程是双向的, 一个节点既有向前连接的引用, 也有一个向后连接的引用.我们在查找上一个节点的时候就不用从头开始遍历,根据向前连接的引用就能查找到上一个节点。
但是双向链表有什么缺点呢?
每次在插入或删除某个节点时, 需要处理四个节点的引用, 而不是两个. 也就是相对于单向链表来说实现起来要困难一些, 而且必然占用内存空间更大一些.但是这些缺点和我们使用起来的方便程度相比, 是微不足道的.
双向链表图解:
双向链表的封装:
<script>
function DoublyLinkedList() {
//创建节点构造函数
function Node(data) {
this.prev = null;
this.data = data;
this.next = null;
}
//属性
this.head = null;
this.tail = null;
this.length = 0;
//1.向列表尾部添加一个新的项
DoublyLinkedList.prototype.append = function (data) {
let newNode = new Node(data);
if (this.length === 0){
this.head = newNode;
this.tail = newNode;
}else{
newNode.prev = this.tail;
this.tail.next = newNode;
this.tail = newNode;
}
this.length += 1;
};
//2.向列表特定位置插入一个新的项
DoublyLinkedList.prototype.insert = function (position,data) {
//越界判断
if(position<0 || position>this.length) return false;
//创建新的节点
let 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{ //其他情况
let current = this.head;
let index = 0;
while (index < position){
current = current.next;
index ++;
}
newNode.prev = current.prev;
newNode.next = current;
current.prev.next = newNode;
current.prev = newNode; //一直都在最后一步更新新的节点
}
}
this.length += 1;
return true;
};
//3.获取对应位置的元素
DoublyLinkedList.prototype.get = function (position) {
if(position<0 || position>=this.length) return false; //这里的取值position不能等于length因为在length处没有数据
//为了节省效率,可以采取以下方法
// this.length / 2 < position :采用从前往后遍历
// this.length / 2 > position :采用从后往前遍历
if(this.length / 2 < position){
let index = 0;
let current = this.head;
while (index < position){
current = current.next;
index ++;
}
return current.data;
}else{
let index = this.length - 1;
let current = this.tail;
while (index > position){
current = current.prev;
index --;
}
return current.data;
}
};
//4.返回元素在列表中的索引,如果没有该元素则返回-1
DoublyLinkedList.prototype.indexof = function (data) {
let index = 0;
let current = this.head;
while (current){
if (current.data === data){
return index;
}else{
current = current.next;
index += 1;
}
}
return -1;
};
//5.修改某个位置的元素
DoublyLinkedList.prototype.updata = function (position,newData) {
if(position < 0 || position >= this.length) return false;
let index = 0;
let current = this.head;
while (index ++ < position){
current = current.next;
}
current.data = newData;
return true;
};
//6.从列表指定位置移除一项,根据位置
DoublyLinkedList.prototype.removeAt = function (position) {
if (position < 0 || position >= this.length) return null;
let 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){
let current = this.tail;
this.tail.prev.next = null;
this.tail = this.tail.prev;
}else{
let index = 0;
while(index ++ < position){
current = current.next;
}
current.prev.next = current.next;
current.next.prev = current.prev;
}
}
this.length -= 1;
return current.data; //返回被删除节点的数据
};
//7.从列表中移除一项,根据数据
DoublyLinkedList.prototype.remove = function (data) {
this.removeAt(this.indexof(data));
};
//8.判断链表是否为空
DoublyLinkedList.prototype.isEmpty = function () {
return this.length === 0;
};
//9.返回链表的长度
DoublyLinkedList.prototype.size = function () {
return this.length;
};
//10.tostring()方法
DoublyLinkedList.prototype.tostring= function () {
return this.backwordString;
};
//11.返回正向遍历的节点字符串形式,将链表里面的数据从后往前遍历并返回字符串形式
DoublyLinkedList.prototype.forwardString = function () {
let resultString = '';
let current = this.tail;
while (current){
resultString += current.data + ' ';
current = current.prev;
}
return resultString;
};
//12.返回反向遍历的节点字符串形式,将链表里面的数据从前往后遍历并返回字符串形式
DoublyLinkedList.prototype.backwordString = function () {
let resultString = '';
let current = this.head;
while (current){
resultString += current.data + ' ';
current = current.next;
}
return resultString;
};
}