双向链表是基于单向链表的,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。
1、创建Node类
function Node(element) {
this.element=element;
this.next = null;
this.previous=null;
}
2、查找最后的节点
其实,这里的问题主要就是如何判断链表的最后节点,如果当前节点的下一个节点为空,是不是就说明了我后面已经没有节点了,那我自然就是最后一个。所以,这里就通过一个while循环不断往后推,只要你不是,那我就找你的下一个,直到你的后面为空,我就找到了。
this.lastNode = function () {
var lastNode = this.head;
while (lastNode.next != null) {
lastNode = lastNode.next;
}
return lastNode;
}
3、查找节点
首先,定义node节点为头结点,如果node不为空并且它的节点元素不等于我要找的元素,那么就让node节点等于它的下一个节点元素,继续判断是否符合循环条件。简单来说,就是从头挨个挨个找,找到我就跳出循环,返回node,找不到最后还是返回node,不过此时的node已经为空了。
this.find = function (element) {
var node = this.head;
while (node != null && node.element != element) {
node = node.next;
}
return node;
}
4、在after后插入节点
this.insertAfter = function (element, after) {
//找到当前节点
var node = this.find(after);
//创建新的节点
var newNode = new Node(element);
//新节点和当前节点的后一个节点连接在一起
if (node.next != null)
node.next.previous=newNode;
newNode.next = node.next;
//连接当前节点和after节点
node.next = newNode;
newNode.previous = node;
}
5、在before前插入节点
this.insertBefore = function (element, before) {
//找到当前节点
var node = this.find(before);
if(node.previous==null){
return;
}
//创建新的节点
var newNode = new Node(element);
//新节点和当前节点的前一个节点连接在一起;
node.previous.next = newNode;
newNode.previous = node.previous;
//连接当前节点和before节点
node.previous = newNode;
newNode.next = node;
}
6、删除节点
this.remove = function (element) {
//获得当前的节点
var node = this.find(element);
//获得当前节点的前一个节点
var preNode = node.previous;
//获得当前节点的后一个节点
var nextNode = node.next;
//连接节点
preNode.next = nextNode;
if (nextNode != null) {
preNode = nextNode.previous;
}
}
7、遍历链表
this.forEach = function (call) {
var node = this.head;
while (node != null) {
call(node);
node = node.next;
}
}
8、反向遍历链表
this.forEach = function (call) {
var node = this.head;
while (node != null) {
call(node);
node = node.next;
}
}
9、当前节点向前移动n个节点
这和之前说过的单向链表的advance方法很相似,我们不妨想一想,既然是双向,肯定是前面的指向后面的,后面的指向前面的,接下来,我们将通过一个实例来解释:
双向链表中:head 1 2 3 4 5
现在我要将3往前移动5个节点,currElement等于3,n等于5,其实,现在我们已经知道了最后的结果是head 3 1 2 4 5
(1)调用查找节点的方法,获得当前的节点3;
(2)获得当前节点的前一个节点,所以,buffer等于3的前一个节点2,preNode等于buffer也等于2;
(3)如果preNode的元素不等于头结点,这是因为如果等于头结点说明已经不能往前走了,再走就走出去了;n大于0表明我还没移动5个节点。当这两个条件同时满足后,就让preNode等于它的前一个节点,同时n要减一,因为我已经往前移动一位了。
结合我们上面说的,现在我的preNode应该是等于1,n=4,那我什么时候跳出循环呢?在下一次循环的时候,preNode应该等于头结点,n=3,这个时候我的preNode元素已经不再满足循环条件,跳出循环;
(4)如果当前节点的下一个节点不为空,让buffer的下一个节点指向当前节点currentNode的下一个节点,即让2的下一个节点指向3的下一个节点,其实就是将当前节点的前一个节点和它的后一个节点连接在一起,如果是单向链表,这一步就算是结束了,但是对于双向链表来说,还要让当前节点currentNode的下一个节点的前一个节点等于buffer,实际上就是3的下一个节点4的前一个节点等于2,简单来说,就是我的后面是你,你的前面是我。
(5)当前节点currentNode的下一个节点等于preNode的下一个节点,当前节点currentNode的下一个节点的前一个节点等于当前节点currentNode。
当前节点是5,preNode我们循环出来是头结点,5的下一个节点指向的是头结点的下一个节点,说明头结点后面是5;头结点的下一个节点的前一个节点等于头结点,其实就是头结点本身。
(6)preNode的下一个节点等于当前节点,当前节点的前一个节点等于preNode。
this.advance=function (currElement,n){
//获得当前节点
var currentNode = this.find(currElement);
//获得当前节点的前一个节点
var buffer = currentNode.previous;
var preNode = buffer;
while(preNode.element!='head'&&n>0){
preNode = preNode.previous;
n--;
}
if(currentNode.next!=null){
buffer.next=currentNode.next;
currentNode.next.previous = buffer;
currentNode.next=preNode.next;
preNode.next.previous = currentNode;
preNode.next=currentNode;
currentNode.previous=preNode;
}
}
10、当前节点向后移动n个节点
this.back=function (currElement,n){
//获得当前节点
var currentNode = this.find(currElement);
//假设当前节点为最后节点
var lastNode = currentNode;
//在n>0的条件下,下一个节点为空的时候停下来
while(lastNode.next!=null&&n>0){
lastNode = lastNode.next;
n--;
}
//currentNode节点和lastNode相等,说明currentNode本身就是最后的节点
if(currentNode==lastNode){
return;
}
//当前节点的前一个节点和当前节点的后一个节点连接在一起
currentNode.previous.next = currentNode.next;
currentNode.next.previous=currentNode.previous;
//把当前节点连在lastNode节点后面
currentNode.next=lastNode.next;
lastNode.next=currentNode;
currentNode.previous=lastNode;
currentNode.next.previous=currentNode;
}
}