数据结构之双链表

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

    }
}
发布了46 篇原创文章 · 获赞 49 · 访问量 7万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章