数据结构之双链表代码实现

一 什么是双链表

双链表是一种基本数据结构。每个数据结构都包含三部分:前指针,当前数据值,后指针。前指针用来指向前一个元素即前驱,后指针用来后一个元素即后继。这样就形成了链条。
其节点结构图如下:
在这里插入图片描述
其数据结构图如下:
在这里插入图片描述

如果first的前指针指向last的元素且last的后指针指向first元素则形成双向循环链表。在JDK7以前LinkedList是双向循环链表,JDK7则改成了非循环双链表。所以在浏览些老的文章说其为循环双链表也不为过。
本文的主角LinkedList就是双链表结构
内部声明了Node类型的first和last属性,默认值为null
first 节点也是头节点, last节点也是尾节点

transient int size = 0; // 链表的容量

transient Node<E> first; // 指向第一个节点

transient Node<E> last; // 指向最后一个节点

每一个链表都是一个Node节点,由三个元素组成:

private static class Node<E> {
  		// Node节点的元素值
        E item;
  		// 指向下一个元素
        Node<E> next;
  		// 指向上一个元素
        Node<E> prev;

  		// 节点构造函数
        Node(Node<E> prev, E element, Node<E> next) {
            this.item = element;
            this.next = next;
            this.prev = prev;
        }
}

二、LinkedList的特点

  • 查到列表中的元素,需要从头至尾的遍历列表
  • 没有实现同步
  • 每个元素都是一个节点,它保留对下一个和前一个节点的引用
  • 插入和删除效率较高,查询效率较差
  • 维护了插入的顺序,可以按插入顺序遍历
  • 虽然没有实现同步但可以调用Collections.synchronizedList实现同步
 List list = Collections.synchronizedList(LinkedList(...));

面试题:ArrayList LinkedList Vector 的异同?
相同:都实现了List的接口,存储数据特点相同–存储有序的可重复的数据.
不同:ArrayList:作为List接口的主要实现类,线程不安全,执行效率较高;LinkedList:底层使用双向链表存储;对于频繁的插入、删除操作使用频率较高Vector作为List的接口的古老实现类,线程安全,效率较低.

三、重要方法解析

1 add(E e)
默认尾部添加元素

public boolean add(E e) {
          linkLast(e);
          return true;
    }
  // 真正添加节点的操作
    void linkLast(E e) {
      final Node<E> l = last;//获取尾部元素
        // 生成一个Node节点,并将此节点的前指针指向last元素
      final Node<E> newNode = new Node<>(l, e, null);
      last = newNode;
        // 如果l = null,代表的是第一个节点,所以这个节点即是头节点
        // 又是尾节点
      if (l == null)
          first = newNode;//设置新的首元素为新节点
      else
        // 如果不是的话,那么就让该节点的next 指向新的节点
          l.next = newNode;
      size++;
      modCount++;
  	}

2 add(int index,E e) :
指定index位置插入元素

 public void add(int index, E element) {
        // 检查index合法性
        checkPositionIndex(index);

        if (index == size)
          	// 如果需要插入的位置和链表的长度相同,就在链表的最后添加
            linkLast(element);
        else
          	// 否则就链接在此位置的前面
            linkBefore(element, node(index));
    }

	
    // 越界检查
    private void checkPositionIndex(int index) {
          if (!isPositionIndex(index))
              throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

    // 判断参数是否是有效位置(对于迭代或者添加操作来说)
    private boolean isPositionIndex(int index) {
        return index >= 0 && index <= size;
    }
    // 查找索引所在的节点
    Node<E> node(int index) {
      //将链表长度折成两半,比index小的从左边查找,比index大的从右边,循环直到找到为止
        if (index < (size >> 1)) {//比index小的从左边查找
            Node<E> x = first;
            for (int i = 0; i < index; i++)
                x = x.next;
            return x;
        } else {//比index大的从右边
            Node<E> x = last;
            for (int i = size - 1; i > index; i--)
                x = x.prev;
            return x;
        }
    }
    // 在非空节点插入元素
    void linkBefore(E e, Node<E> succ) {
      	// succ 即是插入位置的节点
    		// 查找该位置处的前面一个节点
        final Node<E> pred = succ.prev;//获取当前succ的前一个节点
        final Node<E> newNode = new Node<>(pred, e, succ);//构建新的节点并将前指针指向pred
        succ.prev = newNode;//succ前指针指向新的节点
        if (pred == null)//在链表头部添加
            first = newNode;
        else//在链表中间添加
            pred.next = newNode;
        size++;
        modCount++;
    }

3 addAll(int index, Collection<? extends E> c)
指定位置添加集合

 public boolean addAll(int index, Collection<? extends E> c) {
 checkPositionIndex(index);
 //为什么这么做?--1传入的集合可能是ArrayList,linkedList,Vector,栈等等,每个数据类型
 //不一样,转换成数组,便于将其转成双链表结构方便插入;2 数组查询遍历快提高性能
        Object[] a = c.toArray();
        int numNew = a.length;
        if (numNew == 0)
            return false;
        Node<E> pred, succ;
        if (index == size) {//就是尾部插入
            succ = null;
            pred = last;//获取last元素①
        } else {//头部或者中间插入
            succ = node(index);//当前插入节点
            pred = succ.prev;
        }
        for (Object o : a) {
            @SuppressWarnings("unchecked") E e = (E) o;
            //只能确定insertNode的前指针是pred,后指针暂时无法确定
            Node<E> newNode = new Node<>(pred, e, null);
            if (pred == null)//加入了插入位置是头部的情况
                first = newNode;
            else//将①位置的后指针指向当前创建的元素newNode;
             //下一次循环会将②位置的前一次循环的newNode的后指针指向本次循环创建的newNode 
                pred.next = newNode;
        //pre赋予当前newNode;②--将元素串成链表的关键;循环结束pred变成此链表的最后一位③
            pred = newNode;
        }

        if (succ == null) {//说明index位置是原链表的最后一位元素
            last = pred;//将③位置的元素作为last元素
        } else {//将链表最后元素与index位置的元素建立联系
            pred.next = succ;
            succ.prev = pred;
        }
        size += numNew;
        modCount++;
        return true;
}

4 E remove(int index)
按索引进行删除

public E remove(int index) {
        checkElementIndex(index);
        return unlink(node(index));
    }
    //删除核心
E unlink(Node<E> x) {
        // assert x != null;
        final E element = x.item;//当前删除元素的值
        final Node<E> next = x.next;//前一个节点
        final Node<E> prev = x.prev;//后一个节点

        if (prev == null) {//代表删除节点是头结点
            first = next;//first指向下一个节点
        } else {//从中间删除
            prev.next = next;//上一个节点的后指针指向下一个节点
            x.prev = null;//置空当前删除节点的前指针
        }

        if (next == null) {//代表删除节点是尾结点
            last = prev;//last指向前一个节点
        } else {//从中间删除
            next.prev = prev;//下一个节点的前指针指向前一个节点
            x.next = null;//置空当前删除节点的后指针
        }

        x.item = null;//置空元素的节点的值
        size--;
        modCount++;
        return element;
    }

四、模拟LinkedList的完整代码实现

以下代码实现了linkedList的绝大部分功能,并拓展了其部分功能。完整代码如下

package aiguigu.collectionExer;
import java.util.*;
import java.util.function.Predicate;

/**
 * @author Jerssy
 * @version V1.0
 * @Description:双链表代码实现
 * @create 2020-11-25 16:49
 */
public class MyLinkedList<E> {

    private Node<E> first;//链表头部节点
    private Node<E> last;//链表尾部节点
    private int count;//记录元素个数

    public MyLinkedList() {
    }

    //内部节点类用来构建数据
    private static class Node<E>{
        Node<E> prev;//前指针保存当前链表节点前一个节点数据
        Node<E> next;//后指针保存当前链表节点后一个节点数据
        E item;//当前节点数据

        Node(Node<E> prev, E item,Node<E> next) {
            this.item = item;
            this.next = next;
            this.prev = prev;
        }
    }

    @Override
    public String toString() {
        Node<E> pre = first;
        StringBuilder sb = new StringBuilder();
        sb.append('[');
        for (int i = 0; i < count; i++) {
            sb.append(pre.item);
            pre = pre.next;
            if (i == count - 1) {
                break;
            }
            sb.append(',');
        }
        sb.append(']');
        return sb.toString();

    }

    //添加元素,双链表默认从尾部添加
    public void addNode(E e){
        addLast(e);
    }

    //头部添加与尾部添加类似
    public void addFirst(E e){
        addOfFirst(e);
    }

    //尾部添加
    private  void addLast(E e){
        //这里可以插入null元素,双链表支持null元素的插入
        //第一步:获取尾部节点last
        Node<E> lastNode= last;
        //第二步 addData前指针指向lastNode,addData后指针置空,构建Node节点类
        Node<E> addData=new Node<>(lastNode,e,null);
        //第三步:当前addData变成last节点
         last=addData;
        if(null==lastNode){//说明链表是空的,将first元素赋予addData;
            first=addData;
        }
        else{
            lastNode.next=addData;//将lastNode的尾部指针指向addData
        }
        count++;
    }

    //头部添加
    private void addOfFirst(E e){
        //尾部一样--找头元素,构建节点Node,将插入元素变成first节点,最后将插入元素尾部指针指向原来的first节点
         Node<E> firstNode=first;
         Node<E> addData=new Node<>(null,e,firstNode);
         first=addData;
         if(null==firstNode){//说明链表是空的,将last元素赋予addData;
             last=addData;
         }
         else{
             firstNode.prev=addData;//将firstNode的头部指针指向addData
         }
        count++;
    }
    //从指定位置添加元素
    public void addInsert(E e, int index){
      //看看插入index是否合法,然后找插入的位置,断开此位置的前与后连接
        if(index<0||index>count){//不合法抛个异常
            throw new IndexOutOfBoundsException("参数不合法,请检查参数");
        }
        else if(index==0){//不就是头部插入嘛
            addFirst(e);
        }
        else if(index==count){//不就是尾部插入嘛
            addLast(e);
        }
        else{//中间插入
            //找插入位置index的元素
            Node<E> indexNode=searchNodeOfIndex(index);
            //将待插入节点的前指针指向indexNode的前一个元素,后指针指向indexNode
            Node<E> insertNode=new Node<>(indexNode.prev,e,indexNode);
            indexNode.prev.next=insertNode;//indexNode的前一个元素的后指针指向插入节点insertNode
            indexNode.prev=insertNode;//indexNode的前指针指向插入节点insertNode
        }
        count++;
    }

    //俩表尾部插入集合
    public void addAllNode(Collection<? extends E> e){
        addAll(e, count);
    }

    //指定位置插入集合
    public void addAllNode(Collection<? extends E> e, int index){
        addAll(e, index);
    }
    private void addAll(Collection<? extends E> e, int index){
        //不合法抛异常
        if(index<0||index>count){//不合法抛个异常
            throw new IndexOutOfBoundsException("参数不合法,请检查参数");
        }
        if(null==e){
            throw new NullPointerException("集合为空");
        }

        //1 先将插入集合转成数组--为什么这么做?--1传入的集合可能是ArrayList,linkedList,Vector,栈等等,每个数据类型
        //不一样,转换成数组,便于将其转成双链表结构方便插入;2 数组查询遍历快提高性能
        Object[] objArray =e.toArray();
        if(objArray.length==0){
           return;
        }

        //方法一:先数组转成双链表
        //2先将arrays数据转成双链表,拿到此链表的first和last元素进行操作
        @SuppressWarnings("unchecked") E s = (E) objArray[0];
        Node<E> predNode=new Node<>(null,s,null);
        Node<E> firstNode=predNode;//保存第一个元素后续操作
        for (int i = 1; i < objArray.length; i++) {
            @SuppressWarnings("unchecked") E a = (E) objArray[i];
            Node<E> insertNode = new Node<>(predNode, a, null);
            predNode.next=insertNode;
            predNode=insertNode;//循环结束predNode变成此链表的最后一位
        }
         Node<E> insertFirst=firstNode.next.prev;//获取新链表的first元素
        //3 判断插入位置
        if(index==count){//就是尾部插入嘛
              last.next=insertFirst;
              insertFirst.prev=last;
              last=predNode;//更新last元素
        }
        else if(index==0){//就是头部插入嘛
            predNode.next=first;
            first.prev=predNode;
            first=firstNode;//更新first元素
        }
        else{//中间插入
            Node<E> indexNode =searchNodeOfIndex(index);
            indexNode.prev.next=insertFirst;//indexNode的前一元素后指针指向insertFirst
            insertFirst.prev=indexNode;//insertFirst的前指针指向indexNode
            predNode.next=indexNode;//新链表的后元素的后指针指向indexNode
            indexNode.prev=predNode;//indexNode的前指针指向新链表的后元素。
        }

        //方法二:JDK实现
       /* Node<E> sucNode=null;
        Node<E> predNode;
        if(index==count){//就是尾部插入嘛
           predNode=last;//获取last元素①
        }
        else{//头部或者中间插入
            sucNode =searchNodeOfIndex(index);//找index位置的元素
            predNode=sucNode.prev;//此位置的前一个元素①
        }
           for (Object o : objArray) {
               @SuppressWarnings("unchecked") E s = (E) o;
                Node<E> insertNode=new Node<>(predNode,s,null);//只能确定insertNode的前指针是predNode,后指针暂时无法确定
                if(null==predNode){//加入了插入位置是头部的情况
                    first=insertNode;
                }
                else{
                    predNode.next=insertNode;//将①位置的后指针指向当前创建的元素insertNode;
                    //下一次循环会将②位置的前一次循环的insertNode的后指针指向本次循环创建的insertNode
                }
                predNode=insertNode;//predNode赋予当前insertNode;②--将元素串成链表的关键;循环结束predNode变成此链表的最后一位③

            }
            if(null==sucNode){//说明index位置是原链表的最后一位元素
                last=predNode;//将③位置的元素作为last元素
            }
            else{//将链表最后元素与index位置的元素建立联系
                predNode.next=sucNode;
                sucNode.prev=predNode;
            }*/
        count+=objArray.length;
    }

    //根据元素值获取索引--可以有重复数据这里用数组存储索引
    public  List<Integer> getNodeOfIndex(E e){
        ArrayList<Integer> result=new ArrayList<>();
        int i = 0;
        Node<E> node=first;
        while (i<count){
            if(e==null&&node.item==null||e!=null&&e.equals(node.item)){//考虑下元素可能为null的情况
                result.add(i);
            }
            node=node.next;
            i++;
        }
        return  result;
    }

    //找指定索引index位置的元素
   public Node<E> searchNodeOfIndex(int index){
        Node<E> result;
        //将数组或者链表长度折成两半,比index小的从左边查找,比index大的从右边,循环直到找到为止
        int leftIndex=count>>1;//位运算,相当于除以2;
       Node<E> node;
       int i;
       if(index<leftIndex){//比index小的从左边查找
           node = first;
           i = 0;
             while (i<index){
                 node=node.next;
                 i++;
             }
       }
        else{//比index大的从右边开始查找
           node = last;
           i = count - 1;
            while (i>index){
                node=node.prev;
                i--;
            }
       }
       result=node;
       return result;
    }

    //删除前元素
     public E removeFirst(){
        if(first==null){
            return null;
        }
        E oldValue=first.item;
        if(first.next==null){//表里就一个元素
            first=last=null;
        }
        else{
            Node<E> firstNode;
            firstNode=first.next;//新的first元素
            first=null;
            first=firstNode;//设置新的first元素
            first.prev=null;//将新顶上来的元素前指针置空
        }
        count--;
        return  oldValue;
     }

     //删除后元素
    public E removeLast(){
        if(last==null){//表可能是空的
            return null;
        }
        E oldValue=last.item;
        if(last.prev==null){//表里就一个元素
            first=last=null;
        }
        else {
            Node<E> lastNode;
            lastNode=last.prev;
            last=null;
            last=lastNode;
            last.next=null;
        }
        count--;
        return  oldValue;
    }
 // 从指定位置删除指定个数的元素
    public List<E> removeRange( int index,int limit){//从指定位置(包含指定位置元素·)开始删除limit个元素
        ArrayList<E> result=new ArrayList<>();
        //不合法抛异常
        if(index<0||index>=count||limit<0||(index+limit)>count){//不合法抛个异常
            throw new IndexOutOfBoundsException("参数不合法,请检查参数");
        }
         if(limit>0){//确保要删除数据且limit代表删除几个
             if(first.next==null){//只有1个元素
                 result.add(first.item);
                 first=last=null;
             }
             else{
                 //找limit位置的下一个元素
                 int limitIndex;
                 if(index==0){
                     limitIndex = limit;
                 }
                 else{
                     limitIndex=(limit+index==count)?count-1:limit+index;//limit位置不能超过last位置
                 }
                 Node<E> indexNode=searchNodeOfIndex(index);//删除开始位置元素
                 Node<E> limitNode=searchNodeOfIndex(limitIndex);//删除结束位置下一个元素
                 Node<E> indexPrev=indexNode.prev;//记录删除前一位元素
                 int i = 0;
                 while (i<limit){//删除index到limit位置所有元素
                     Node<E> newNode=indexNode;
                     result.add(newNode.item);
                     indexNode.prev=null;
                     newNode=newNode.next;
                     indexNode.next=null;
                     indexNode=newNode;
                     i++;
                 }
                 if(indexPrev==null){//删除了first元素,新的first是删除结束位置的下一位置元素
                     first=limitNode;
                 }
                 else{//从中间删除元素
                     indexPrev.next=limitNode;
                     limitNode.prev=indexPrev;
                 }
                 if(limitNode.next==null){//删除元素包含last元素,新的last是删除开始位置的前一位置元素
                     last=indexPrev;
                 }
             }
             count-=limit;
         }
        return  result;
    }

    //删除指定位置元素
    public List<E> remove(int index){
        return  removeRange(index,1);
    }

    //删除某个元素值
   public boolean remove(Object obj){
        Node<E> removeNode=first;
        while (removeNode!=null){
            if(obj==null){//元素值可能为空的情况
                if(removeNode.item==null){
                    return remove(removeNode);
                }
            }
            else{
                if(obj.equals(removeNode.item)){
                    return remove(removeNode);
                }
            }
            removeNode=removeNode.next;
        }
        return false;
   }

   private  boolean remove(Node<E> node){
        //获取此位置的前一个元素和后一个元素
         Node<E> nodePrev=node.prev;
         Node<E> nodeSuc=node.next;
         node.item=null;
         node.prev=null;
         node.next=null;
         if(null==nodePrev){//删除first元素
             first=nodeSuc;
             first.prev=null;//将新顶上来的元素前指针置空
         }
         else{
             nodePrev.next=nodeSuc;
         }
         if(null==nodeSuc){//删除了last元素
             last=nodePrev;
             last.next=null;//将新顶上来的元素后指针置空
         }
         else{
             nodeSuc.prev=nodePrev;
         }
         count--;
        return true;
   }

   //按过滤条件删除
   public  void removeIf(Predicate<? super E> filter){
       Node<E> flagNode=first;Node<E> removeNode;
       while (flagNode!=null) {
           if(filter.test((removeNode=flagNode).item)){//记录删除位置的元素
               remove(removeNode.item);
           }
           flagNode=flagNode.next;
       }
   }

   //判断某元素从fromIndex开始首次出现的位置
    public int indexOf(E e, int fromIndex){
        if(fromIndex<0){
            fromIndex=0;
        }
        if(fromIndex<count){
             Node<E> indexNode=searchNodeOfIndex(fromIndex);
            for (int i = fromIndex; i < count; i++) {
                 if(e==null&&indexNode.item==null||e!=null&&e.equals(indexNode.item)){
                     return i;
                 }
                indexNode=indexNode.next;
            }
        }
        return -1;
    }
    //判断某元素首次出现的位置
    public int indexOf(E e){
        return  indexOf(e,0);
    }
    //判断某元素从fromIndex开始反向搜索最后次出现的位置
    public int lastIndexOf(E e, int fromIndex){
        if(fromIndex<0){
            fromIndex=0;
        }
        if(fromIndex>=count){
            fromIndex=count-1;
        }
        Node<E> indexNode=searchNodeOfIndex(fromIndex);
        for (int i = fromIndex; i>0; i--) {
            if(e==null&&indexNode.item==null||e!=null&&e.equals(indexNode.item)){
                return i;
            }
            indexNode=indexNode.prev;
        }
        return -1;
    }
    //判断某元素最后此出现的位置
    public int lastIndexOf(E e){
        return  lastIndexOf(e,count-1);
    }

    //修改index位置元素的值
    public E setValue(int index,E element){
        if(index<0||index>count-1){
            throw new IndexOutOfBoundsException("参数不合法");
        }
        Node<E> indexNode=searchNodeOfIndex(index);
        E oldValue=indexNode.item;
        indexNode.item=element;
        return  oldValue;
    }
}
package aiguigu.collectionExer;
import java.util.ArrayList;
import java.util.Date;
/**
 * @author Jerssy
 * @version V1.0
 * @Description
 * @create 2020-11-26 21:31
 */
public class myLinkedTest   {
    public static void main(String[] args) {
        MyLinkedList<Object> list = new MyLinkedList<>();
        ArrayList<Object> arrayList=new ArrayList<>();
        arrayList.add("tom");
        arrayList.add("tom");
        arrayList.add("tom");
        arrayList.add("leo");
        arrayList.add("jack");
        arrayList.add(null);
        arrayList.add(new Date());
        System.out.println("**********添加元素********");
        list.addNode(2);
        list.addNode(3);
        System.out.println(list);
        System.out.println("**********头部添加元素********");
        list.addFirst("你好");
        System.out.println(list);
        System.out.println("**********指定1位置添加collection集合********");
        list.addAllNode(arrayList,1);
        System.out.println(list);
        System.out.println("**********遍历集合********");
        System.out.println(list);
        System.out.println("**********3位置插入kobe元素********");
        list.addInsert("kobe",3);
        System.out.println(list);
        System.out.println("**********修改2位置元素的值为jrSmith,并返回原来的值********");
        System.out.println(list.setValue(2,"jrSmith"));
        System.out.println(list);
        System.out.println("**********删除第一个,并返回原来的值********");
        System.out.println(list.removeFirst());
        System.out.println(list);
        System.out.println("**********删除最后一个,并返回原来的值********");
        System.out.println(list.removeLast());
        System.out.println(list);
        System.out.println("**********判断tom元素从2开始在链表首次出现的位置********");
        System.out.println(list.indexOf("tom",2));
        System.out.println("**********判断tom元素在链表中首次出现的位置********");
        System.out.println(list.indexOf("tom"));
        System.out.println("**********反向搜索tom元素从7开始在链表中最后次出现的位置********");
        System.out.println(list.lastIndexOf("tom",7));
        System.out.println("**********判断tom元素在链表最后出现的位置********");
        System.out.println(list.lastIndexOf("tom"));
        System.out.println("**********根据元素tom值获取索引********");
        System.out.println(list.getNodeOfIndex("tom"));
        System.out.println("**********从2的位置删除3个元素********");
        System.out.println(list.removeRange(2 ,3));
        System.out.println(list);
        System.out.println("**********删除3位置的元素********");
        System.out.println(list.remove(3));
        System.out.println(list);
        System.out.println("**********删除2元素,要用包装类修饰否则会按索引删除********");
        System.out.println(list.remove(Integer.valueOf(2)));
        System.out.println(list);
        System.out.println("**********删除满足条件的所有元素,需要指定泛型********");
        MyLinkedList<Integer> list2 = new MyLinkedList<>();
        list2.addNode(12);
        list2.addNode(3);
        list2.addNode(14);
        list2.addNode(17);
        list2.removeIf(e->e>12);//删除满足条件的所有元素
        System.out.println(list2);
    }
}

在这里插入图片描述
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值