在单链表分析中,每个结点只有一个指向后继结点的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));
}
//其他的删除方法类似,直接调用即可。
}
}