在單鏈表分析中,每個結點只有一個指向後繼結點的next域,如果此時已知當前結點,需要查找其前驅結點,那麼必須從head頭指針遍歷至p的前驅結點,操作效率很低,因此如果p指向前驅結點的next的域,效率就會提升很多,因此就出現了雙鏈表。
雙鏈表是鏈表的一種。它由結點組成,它的每個數據結點中都有兩個指針,分別指向直接後繼和直接前驅。所以,從雙鏈表的任意一個結點開始,都可以很方便的訪問他的前驅結點和後繼結點。
雙鏈表的結點示意圖如下:
雙鏈表的示意圖如下:
雙鏈表的插入操作分析
雙鏈表的插入一般分爲三種情況:
1)插入空雙鏈表
2)插入雙鏈表的尾部
3)雙鏈表的中間插入
從上圖可以看出第一種和第二種情況實際屬於一種情況,只需要注意front.next != null 的情況而第三種情況就不需要注意這種情況,因爲在中間插入的時候無論何時它的後繼結點都不會爲空。
插入操作實際上就是把原來的鏈接打斷,然後把前一個結點的next域指向先插入節點的pre域,將新插入的結點next域指向後一個結點的pre域。
雙鏈表刪除操作分析
雙鏈表的刪除操作也分爲兩種情況:
1)雙鏈表的尾部刪除操作
2)雙鏈表的中間結點刪除操作
第一種情況只要注意p.next.pre拋空指針的情況,而對於第二種情況不需要關心。
雙鏈表的刪除操作實際就是把要刪除結點p的pre域和next域都斷開,然後把p前後的結點連接在一起。
雙鏈表的查詢分析:
雙鏈表的查詢操作與單鏈表的查詢操作相似,只需要查找的當前結點獲取他的data即可。
循環雙鏈表就是雙鏈表的最後一個結點的next域指向頭結點,而頭結點的prev指針指向最後一個結點。
在循環雙鏈表中我們不再需要尾指向結點,因爲整個鏈表已構成循環,在頭結點head的位置也可以輕鬆獲取到尾部結點的位置。對於循環雙鏈表的插入、刪除操作也無需區分位置操作的情況,這是由於循環雙鏈表的本身的特殊性,使p.next.pre永遠不可能爲null,因此我們在插入和刪除時代碼實現相對簡單些。
用 java 實現鏈表如下:
Step1.定義結點
package test;
/**
* Created by DELL on 2017/12/1.
*/
public class Dnode<T> {
public T data;
public Dnode<T> prev;//前繼指針
public Dnode<T> next;//後繼指針
public Dnode(T data, Dnode<T> prev, Dnode<T> next){
this.data = data;
this.next = next;
this.prev = prev;
}
public Dnode(T data){
this(data,null,null);
}
public Dnode(){
this(null,null,null);
}
public String toString(){
return this.data.toString();
}
}
Step2.定義一個雙鏈表
package test;
/**
* Created by DELL on 2017/12/1.
*/
public class DoubleLinkedList<T> {
private Dnode<T> head ;//不帶數據的頭結點
private int size;//鏈表的大小
public DoubleLinkedList() {
head = new Dnode<>(null,null,null);
head.prev = head.next = head;
size = 0;
}
//返回結點數目
public int count(){
return size;
}
//返回鏈表是否爲空
public boolean isEmpty(){
return size == 0;
}
//獲取第index位置的結點
public Dnode<T> getNode(int index){
if(index<0 || index >= size){
throw new IndexOutOfBoundsException();
}
//正向查找
if(index <= size/2){
Dnode<T> node = head.next;
for(int i=0;i < index; i++){
node = node.next;
}
return node;
}
//反向查找
Dnode<T> rnode = head.next;
int rindex = size - index - 1;
for(int j=0;j<index;j++){
rnode = rnode.prev;
}
return rnode;
}
//獲取第index位置的結點的值
public T get (int index){
return getNode(index).data;
}
//獲取第一個結點的值
public T getFirst(){
return getNode(0).data;
}
//獲取最後一個結點的值
public T getLast(){
return getNode(size-1).data;
}
//將結點插入插入到第index位置之前
public void insert(int index , T t){
if(index == 0){
Dnode<T> node = new Dnode<T>(t,head,head.next);
//讓head的後繼結點的prev域指向新插入的結點
head.next.prev = node;
head.next = node;
//鏈表長度加1
size ++ ;
return ;
}
//獲得該位置的結點
Dnode<T> inode = getNode(index);
//定義一個新插入的結點
Dnode<T> tnode = new Dnode<T>(t, inode.prev, inode);
//讓該位置上的結點的前繼結點的next域指向新插入的結點
inode. prev.next = tnode;
inode.next = tnode;
size++;
return ;
}
//將結點插入到第一個結點處
public void insertFist(T t){
insert(0,t);
}
//將結點追加到鏈表的末尾
public void insertLast(T t){
Dnode dnode = new Dnode<T>(t,head.prev,head);
//head的前繼結點就是鏈表的尾結點
head.prev.next = dnode;
head.prev = dnode;
size++;
}
//刪除index位置的節點
public void del(int index){
Dnode<T> inode = getNode(index);
//該位置上的結點的前繼結點的next域指向該位置結點的後繼結點
inode.prev.next = inode.next;
//該位置上的結點的後繼結點的prev域指向該位置結點的前繼結點
inode.next.prev = inode.prev;
inode = null;
size -- ;
}
//刪除第一個結點
public void delFist(){
del(0);
}
//刪除最後一個結點
public void delLast(){
del(size-1);
}
public static void main (String[] args){
System.out.println("鏈表的插入操作");
int[] array = {10,20,30,40};
//創建雙鏈表
DoubleLinkedList<Integer> doubleLinkedList = new DoubleLinkedList<>();
doubleLinkedList.insert(0,20);//將20插入到第一個位置
doubleLinkedList.insertLast(10);//將10插入到鏈表末尾
doubleLinkedList.insertFist(30);//將30插入鏈表的第一個位置
for(int i=0;i<doubleLinkedList.size;i++){
//輸出鏈表的值
System.out.println(doubleLinkedList.get(i));
}
System.out.println("鏈表的大小爲:"+doubleLinkedList.count());
System.out.println("…………………………………………………………");
System.out.println("鏈表的查找操作");
System.out.println("獲取第一個結點的值:"+doubleLinkedList.getFirst());
System.out.println("獲取最後一個結點的值:"+doubleLinkedList.getLast());
System.out.println("獲取任意結點的值:"+doubleLinkedList.getNode(2));
System.out.println("………………………………………………………………");
System.out.println("鏈表的刪除操作");
System.out.println("刪除第一個結點後的鏈表的值");
doubleLinkedList.delFist();
for(int i=0;i<doubleLinkedList.size;i++){
//輸出鏈表的值
System.out.println(doubleLinkedList.get(i));
}
//其他的刪除方法類似,直接調用即可。
}
}