数据结构之双链表

来源:互联网 发布:acdsee软件 编辑:程序博客网 时间:2024/05/18 02:54

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