数据结构之双向链表详细(java版)

前言

链表是一种线性表,常见的线性表还有栈、队列,本篇主要是分析双向链表的数据结构,以及如何使用java自己实现一个双向链表。在分析双向链表前,有必要先看看单向链表的数据结构图,方便对比双向链表数据结构。

1、单向链表

百度百科:单向链表(单链表)是链表的一种,其特点是链表的链接方向是单向的,对链表的访问要通过顺序读取从头部开始;链表是使用指针进行构造的列表;又称为结点列表,因为链表是由一个个结点组装起来的;其中每个结点都有指针成员变量指向列表中的下一个结点。
在这里插入图片描述
删除操作后:
在这里插入图片描述

2、双向链表

百度百科:双向链表也叫双链表,是链表的一种,它的每个数据结点中都有两个指针,分别指向直接后继和直接前驱。所以,从双向链表中的任意一个结点开始,都可以很方便地访问它的前驱结点和后继结点。一般我们都构造双向循环链表。
在这里插入图片描述
删除操作后:
在这里插入图片描述

3、手写实现

public class CoolList<T> {
    //节点个数
    public int size;
    //头节点
    private SummerNode<T> first;
    //尾节点
    private SummerNode<T> last;

    //定义节点类型
    private static class SummerNode<T> {
        //节点元素
        T t;
        //前一个节点地址
        SummerNode<T> pre;
        //后一个节点地址
        SummerNode<T> next;

        SummerNode(SummerNode<T> pre,T t,SummerNode<T> next){
            this.t = t;
            this.pre = pre;
            this.next = next;
        }
    }

    /**
     * 更新指定位置的元素
     * @param index 位置
     * @param t 元素
     * @return 返回旧值
     */
    public T replace(int index,T t){
        //检查节点是否存在
        checkExistNode(index);
        //获取指定位置节点
        SummerNode<T> exist = node(index);
        T oldValue = exist.t;
        //替换新值
        exist.t = t;
        return oldValue;
    }

    /**
     * 在指定位置之前插入元素
     * @param index
     * @param t
     * @return 成功返回true
     */
    public boolean beforeIndexInsert(int index,T t){
        //检查节点是否存在
        checkExistNode(index);
        //获取节点
        SummerNode<T> node = node(index);
        //前一个节点
        SummerNode<T> pre = node.pre;
        //插入的节点在pre节点和node节点之间
        final SummerNode<T> newNode = new SummerNode<>(pre, t, node);
        node.pre = newNode;
        if(pre == null){
            //如果插入的位置是第一个节点前
            first = newNode;
        } else {
            pre.next = newNode;
        }
        //元素+1
        size++;
        return true;
    }

    //在末尾插入
    public boolean lastInsert(T t){
        final SummerNode<T> l = last;
        final SummerNode<T> newNode = new SummerNode<>(l, t, null);
        last = newNode;
        if(l == null){
            //如果是首次插入元素,设置为第一个节点
            first = newNode;
        } else {
            l.next = newNode;
        }
        size++;
        return true;
    }

    private void checkExistNode(int index){
        if(index < 0 || index > size){
            throw new IndexOutOfBoundsException("Index: "+index+", Size: "+size+";");
        }
    }

    /**
     * 获取指定位置节点元素
     * @param index
     * @return
     */
    public T get(int index){
        checkExistNode(index);
        return node(index).t;
    }

    /**
     * 获取指定位置的节点
     * @param index
     * @return
     */
    SummerNode<T> node(int index){
        //二分法查找,小于size/2,则顺序查找
        if(index < (size >> 1)){
            SummerNode<T> x = first;
            for(int i=0; i<index; i++){
                x = x.next;
            }
            return x;
        } else {
            SummerNode<T> x = last;
            //倒序查找
            for (int i = size-1; i > index; i--){
                x = x.pre;
            }
            return x;
        }
    }

    /**
     * 移除指定位置的节点
     * @param index
     * @return
     */
    public T remove(int index){
        //查找节点是否存在
        checkExistNode(index);
        //删除节点
        return unlink(node(index));
    }


    /**
     * 移除指定元素的节点
     * @param t
     * @return
     */
    public boolean remove(T t){
        //为空
        if(t == null){
            for(SummerNode<T> x=first; x!=null; x=x.next){
                //查出为空的元素,再删除
                if(x.t == null){
                    unlink(x);
                    return true;
                }
            }
        } else {
            //不为空,查出不为空的元素,然后删除
            for(SummerNode<T> x=first; x!=null; x=x.next){
                if(t.equals(x.t)){
                    unlink(x);
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 删除节点
     * @param node
     * @return
     */
    private T unlink(SummerNode<T> node){
        final T type = node.t;
        //删除节点的前一个节点
        final SummerNode<T> pre = node.pre;
        //删除节点的后一个节点
        final SummerNode<T> next = node.next;
        //头结点
        if(pre == null){
            first = next;
        } else {
            pre.next = next;
            node.pre = null;
        }
        //尾结点
        if(next == null){
            last = pre;
        } else {
            next.pre = pre;
            node.next = null;
        }
        node.t = null;
        size--;
        return type;
    }
}

测试代码:

public class CoolListTest {
    public static void main(String[] args) {
        CoolList<String> coolList = new CoolList<>();
        //增加
        coolList.lastInsert("cool");
        coolList.lastInsert("moon");
        coolList.lastInsert("单向链表");
        coolList.lastInsert("的");
        coolList.lastInsert("单向链表");
        //删除
        coolList.remove(2);
        //修改
        coolList.replace(coolList.size - 1,"双向链表");
        //插入
        coolList.beforeIndexInsert(1,"summer");
        StringBuffer sb = new StringBuffer();
        for (int i=0; i<coolList.size; i++){
            //查找
            sb.append(coolList.get(i)).append(" ");
        }
        System.out.println(sb);
    }
}

控制台输出:

cool summer moon 的 双向链表 

结束语

java中LinkedList容器的底层数据结构也是双向链表,直达地址:从源码分析java容器之LinkedList

猜你喜欢

转载自blog.csdn.net/cool_summer_moon/article/details/106288070