相比于单向链表,双向链表的优点是:对于链表中的任意一个节点,可以从两个方向进行操作,在单向链表中,只有获得结点的前驱结点的指针,才能删除该结点。双向链表中,因为每个结点都有指向前驱节点的指针,所以不必知道前驱结点的地址也能进行删除操作。但是也有缺点:每个节点多出了一个额外的指向前驱结点地址的指针,需要更多的空间开销,另外,对于节点的插入或者删除需要操作更多的指针。
1.双向链表的定义
public class DLLNode{
private int data;
private DLLNode next;
private DLLNode prev;
public DLLNode(int data){
this.data = data;
}
public void setData(int data){
this.data = data;
}
public int getData(){
return data;
}
public void setNext(DLLNode next){
this.Next = next;
}
public DLLNode getNext(){
return this.next;
}
public void setPrev(DLLNode prev){
this.prev = prev;
}
public DLLNode getPrev(){
return this.prev;
}
}
2.双向链表插入
双向链表的插入分为以下几种情况
1.在链表的头部插入一个新结点;
在表头插入新结点需要进行两步操作:
第一步:将新节点的前驱结点指向NULL,将后继结点指向链表的表头;
第二部:将原表头结点的前驱结点指向新插入结点。
2.在链表的末尾插入一个新结点;
第一步:插入结点的后继结点指向NULL,前驱结点指向原链表结尾结点;
第二部:原链表结尾结点的后继指针指向新插入结点。
3.在链表中间插入一个新结点。
第一步:遍历链表找到要插入的位置结点;
第二步:插入结点的后继指针指向位置结点的后继结点,前驱结点指向位置结点;
第三步:位置结点的后继结点的前驱指针指向插入结点,位置结点的后继结点指向新插入结点。
DLLNode DLLInsert(DLLNode headNode,DLLNode nodeToInsert,int position){
if (null == headNode)
return nodeToInsert;
int size = getDLLLength(headNode);
if (position > size+1 || position < 1){
System.out.println("插入位置输入错误!");
return headNode;
}
if (1 == position){
nodeToInsert.setNext(headNode);
headNode.setPrevious(nodeToInsert);
return nodeToInsert;
}else{
DLLNode previousNode = headNode;
int count = 1;
while(count < position-1){
previousNode = previousNode.getNext();
count++;
}
DLLNode currentNode = previousNode.getNext();
nodeToInsert.serNext(currentNode);
if(currentNode != null)
currentNode.setPrevious(nodeToInsert);
previousNode.setNext(nodeToInsert);
nodeToInsert.setPrevious(previousNode);
}
return headNode;
}
3.删除双向链表结点
双向链表的删除也分为三种情况:
1.删除表头结点
第一步:创建一个临时结点,它与表头指向同一个节点;
第二步:修改表头结点指针head(表头),使其指向下一个结点,将表头结点的前驱指针改为NULL,然后移除临时结点。
2.删除末尾结点
第一步:遍历链表,在遍历时还要保存前一个节点的地址,当遍历到结尾时,有两个指针分别是指向表尾结点的tail指针和指向表尾结点前一个节点的指针;
第二步:更新表尾的前一个结点的next指针为NULL;
第三步:移除表尾结点。
3.删除双向链表中间的结点
第一步:遍历链表时保持前一个结点,找到要删除的结点,更改前一个节点的后继指针指向被删除结点的下一个节点,更改被删除结点的后继结点的前驱结点指针指向删除结点的前驱结点;
第二步:移除被删除的结点。
DLLNode DLLDelete(DLLNode headNode, int position){
int size = getDLLLength(headNode);
if (position > size || position < 1){
System.out.println("输入位置错误!");
return headNode;
}
if (1 == position){
DLLNode currentNode = headNode.getNext();
headNode = null;
currentNode.setPrevious(null);
return currentNode;
}else{
DLLNode previousNode = headNode;
int count = 1;
while (count < position -1){
previousNode = previousNode.getNext();
count++;
}
DLLNode currentNode = previousNode.getNext();
DLLNode laterNode = currentNode.getNext();
previousNode.setNext(laterNode);
if (laterNode != null)
laterNode.setPrevious(previousNode);
currentNode = null;
}
return headNode;
}
时间复制度为O(n),因为最差的情况下,可能需要删除链表尾部结点。
空间复制度为O(1),仅用于创建一个临时变量。
参考《数据结构与算法经典问题解析:Java语言描述》