LinkedList深入讲解

文章目录

  • [一 : LinkedList的模拟实现](#一 : LinkedList的模拟实现)
    • [1.1 IList接口](#1.1 IList接口)
    • [1.2 基础结构定义](#1.2 基础结构定义)
      • [(1) void display()](#(1) void display())
      • [(2) void addFirst(T data)](#(2) void addFirst(T data))
      • [(3) void addLast(T data)](#(3) void addLast(T data))
      • [(3) boolean contains(T key)](#(3) boolean contains(T key))
      • [(4) int size()](#(4) int size())
      • [(5) void addIndex(int index, T data)](#(5) void addIndex(int index, T data))
      • [(6) void remove(T key)](#(6) void remove(T key))
      • [(7) void removeAllKey(T key)](#(7) void removeAllKey(T key))
      • [(8) void clear()](#(8) void clear())
  • [二: LinkedList详解](#二: LinkedList详解)
    • [2.1 LinkedList 的核心特性](#2.1 LinkedList 的核心特性)
    • [2.2 LinkedList 的构造方法](#2.2 LinkedList 的构造方法)
    • [2.3 LinkedList 的常用方法](#2.3 LinkedList 的常用方法)
    • [2.4 LinkedList 的遍历方式](#2.4 LinkedList 的遍历方式)
    • [2.5 ArrayList 与 LinkedList 的区别](#2.5 ArrayList 与 LinkedList 的区别)

作者:当战神遇到编程

文章专栏:数据结构欢迎大家点赞👍评论📝收藏⭐文章




无头双向链表的基础结构

例子

一 : LinkedList的模拟实现

1.1 IList接口

java 复制代码
public interface IList<T> {

    // 头插法:在链表头部插入数据
    void addFirst(T data) ;

    // 尾插法:在链表尾部插入数据
    void addLast(T data) ;

    // 任意位置插入:第一个节点为0号下标
    void addIndex(int index, T data) ;

    // 查找:判断关键字key是否存在
    boolean contains(T key) ;

    // 删除:移除第一次出现的key节点
    void remove(T key);

    // 删除:移除所有值为key的节点
    void removeAllKey(T key) ;

    // 统计:获取链表长度
    int size() ;

    // 清空:释放链表所有节点
    void clear() ;

    // 遍历:打印链表所有元素
    void display() ;

    //反转链表
    void reverseList();

}

1.2 基础结构定义

java 复制代码
public class MyLinkedList<T> implements IList<T>{
    //静态内部类:链表节点
    static class ListNode<T>{
        public T val;//数据域
        public ListNode<T> prev;//引用域(上一个节点的引用)
        public ListNode<T> next;//引用域(下一个节点的引用)

        public ListNode(T val) {
            this.val = val;
        }
    }
    public ListNode<T> head;//链表的头引用
    public ListNode<T> last;//链表的尾引用


    @Override
    public void addFirst(T data) {

    }

    @Override
    public void addLast(T data) {

    }

    @Override
    public void addIndex(int index, T data) {

    }

    @Override
    public boolean contains(T key) {
        return false;
    }

    @Override
    public void remove(T key) {

    }

    @Override
    public void removeAllKey(T key) {

    }

    @Override
    public int size() {
        return 0;
    }

    @Override
    public void clear() {

    }

    @Override
    public void display() {

    }

    @Override
    public void reverseList() {

    }
}

(1) void display()

java 复制代码
public void display() {
        ListNode<T> cur = this.head;
        while(cur != null) {
            System.out.print(cur.val + " ");
            cur = cur.next;
        }
        System.out.println();
}

(2) void addFirst(T data)

链表不为空时

链表为空时

java 复制代码
public void addFirst(T data) {
        ListNode<T> node = new ListNode<>(data);
        if(this.head == null) {
            this.head = node;
            this.last = node;
        }else{
            node.next = this.head;// 1. 新节点的 next 指向当前的头
            this.head.prev = node;// 2. 当前头的 prev 指向新节点
            this.head = node;// 3. 更新 head 指针指向新节点
        }
}

(3) void addLast(T data)

链表不为空时

链表为空时

java 复制代码
public void addLast(T data) {
        ListNode<T> node = new ListNode<>(data);
        if(this.head == null) {
            this.head = node;
            this.last = node;
        }else {
            this.last.next = node;
            node.prev = this.last;
            this.last = node;
        }
}

(3) boolean contains(T key)

java 复制代码
public boolean contains(T key) {
        ListNode<T> cur = this.head;
        while(cur != null) {
            if(cur.val.equals(key)){
                return true;
            }
            cur = cur.next;
        }
        return false;
}

(4) int size()

java 复制代码
public int size() {
        ListNode<T> cur = this.head;
        int count = 0;
        while(cur != null) {
            count++;
            cur = cur.next;
        }
        return count;
}

下标不合法异常

java 复制代码
public class IllegalIndexException extends RuntimeException{
    public IllegalIndexException() {
    }

    public IllegalIndexException(String message) {
        super(message);
    }
}

(5) void addIndex(int index, T data)

java 复制代码
public void addIndex(int index, T data) {
        int len = size();
        if(index < 0 || index > len) {
            throw new IllegalIndexException("下标不合法异常");
        }
        //头插
        if(index == 0) {
            addFirst(data);
            return;
        }
        //尾插
        if(index == len) {
            addLast(data);
            return;
        }
        //中间位置插
        ListNode<T> cur = search(index);
        ListNode<T> node = new ListNode<>(data);//实例化一个节点
        //修改指向
        node.next = cur;
        cur.prev.next = node;
        node.prev = cur.prev;
        cur.prev = node;
}

/**
  * 找到index位置节点
  * @param index
  * @return
  */
private ListNode<T> search(int index) {
        ListNode<T> cur = this.head;
        int count = 0;
        while(count < index) {
            count++;
            cur = cur.next;
        }
        return cur;
}

(6) void remove(T key)

1.要删除的元素在中间位置

2.要删除的元素在头节点

(1)有很多节点

(2)只有一个节点

)
3.要删除的元素在尾节点

java 复制代码
public void remove(T key) {
        ListNode<T> cur = this.head;
        // 1. 遍历寻找目标节点
        while (cur != null) {
            if (cur.val.equals(key)) {
                //头节点
                if(cur == this.head) {
                    this.head = this.head.next;
                    if(this.head == null) {
                        this.last = null;
                    }else {
                        this.head.prev = null;
                    }
                }else {
                    //尾节点
                    if(cur.next == null) {
                        this.last = this.last.prev;
                        this.last.next = null;
                    }else{
                        //中间节点
                        cur.prev.next = cur.next;
                        cur.next.prev = cur.prev;
                    }
                }
                return;
            }
            cur = cur.next;
        }
}

(7) void removeAllKey(T key)

java 复制代码
public void removeAllKey(T key) {
        ListNode<T> cur = this.head;
        // 1. 遍历寻找目标节点
        while (cur != null) {
            if (cur.val.equals(key)) {
                //头节点
                if(cur == this.head) {
                    this.head = this.head.next;
                    if(this.head == null) {
                        this.last = null;
                    }else {
                        this.head.prev = null;
                    }
                }else {
                    //尾节点
                    if(cur.next == null) {
                        this.last = this.last.prev;
                        this.last.next = null;
                    }else{
                        //中间节点
                        cur.prev.next = cur.next;
                        cur.next.prev = cur.prev;
                    }
                }
            }
            cur = cur.next;
        }
}

(8) void clear()

java 复制代码
 public void clear() {
        ListNode<T> cur = this.head;
        while(cur != null) {
            ListNode<T> nextNode = cur.next;
            cur.val = null;
            cur.prev = null;
            cur.next = null;
            cur = nextNode;
        }
        this.head = null;
        this.last = null;
}

二: LinkedList详解

2.1 LinkedList 的核心特性

1.底层基于无头双向不循环链表 实现.

2.未实现 RandomAccess 接口,其随机访问效率较低,且本身是非线程安全的.

3.在链表中进行任意位置的增删,其性能瓶颈在于查找定位O(n),而非操作本身O(1).

4.ArrayList 采用预分配的连续内存策略,需通过 O(n) 的扩容操作来应对容量增长;而 LinkedList 采用按需分配的离散内存策略,无扩容需求,但以更高的单个节点内存开销和频繁的内存申请为代价

2.2 LinkedList 的构造方法

方法 解释
LinkedList() 无参构造,创建一个空的 LinkedList
LinkedList(Collection<? extends E> c) 创建一个包含指定集合中所有元素的链表

使用示例

java 复制代码
import java.util.LinkedList;
import java.util.List;

public class Test {
    public static void main(String[] args) {
        // 构造空的 LinkedList
        List<String> list1 = new LinkedList<>();
        list1.add("JavaSE");
        list1.add("数据结构");
        list1.add("算法");
        System.out.println(list1);
        
        //构造一个包含指定集合中所有元素的链表
        List<String> list2 = new LinkedList<>(list1);
        System.out.println(list2);


    }
}
//运行结果
[JavaSE, 数据结构, 算法]
[JavaSE, 数据结构, 算法]

2.3 LinkedList 的常用方法

LinkedList 提供了丰富的方法用于操作元素,以下是核心方法:

方法 解释 备注
boolean add(E e) 将元素 e 追加到链表末尾 成功返回 true
void add(int index,E element) 在索引 index 处插入元素 element 索引从 0 开始
boolean addAll(Collection<? extends E> c) 将集合 c 中的所有元素按顺序追加到末尾 只要链表改变就返回 true
E remove(int index) 移除指定索引处的元素,并返回该元素 会触发后续元素前移
boolean remove(Object o) 移除链表中第一个与 o 相等的元素 依据 equals 方法判断
E get(int index) 返回指定索引处的元素 不改变链表结构
E set(int index,E element) 用 element 替换索引 index 处的元素,返回原值 属于更新操作
void clear() 移除链表中的所有元素 链表长度变为 0
boolean contains(Object o) 检查链表中是否包含至少一个元素 o 返回 true/false
int indexOf(Object o) 返回 o 首次出现的索引,不存在则返回 -1 从头向后搜索
int lastIndexOf(Object o) 返回 o 最后一次出现的索引,不存在则返回 -1 从后向前搜索
List< E> subList(int fromIndex,int toIndex) 返回索引从 from 到 to (左闭右开) 的视图 对该视图的修改会反映在原列表中

使用示例

java 复制代码
public class Test {
    public static void main(String[] args) {
        LinkedList<String> linkedList = new LinkedList<>();
        //默认尾插
        linkedList.add("JavaSE");
        linkedList.add("数据结构");
        System.out.println(linkedList);//[JavaSE, 数据结构]

        //尾插
        linkedList.addLast("算法");

        //头插
        linkedList.addFirst("Java之旅");
        System.out.println(linkedList);//[Java之旅, JavaSE, 数据结构, 算法]

        //任意位置插入
        linkedList.add(3,"Mysql");
        System.out.println(linkedList);//[Java之旅, JavaSE, 数据结构, Mysql, 算法]

        //删除指定索引处的元素,并返回该元素
        System.out.println(linkedList.remove(0));//Java之旅
        System.out.println(linkedList);//[JavaSE, 数据结构, Mysql, 算法]

        linkedList.add(0,"Java之旅");
        linkedList.add(1,"Java之旅");
        System.out.println(linkedList);//[Java之旅, Java之旅, JavaSE, 数据结构, Mysql, 算法]

        //移除链表中第一个与o相等的元素
        linkedList.remove("Java之旅");
        System.out.println(linkedList);//[Java之旅, JavaSE, 数据结构, Mysql, 算法]


        //返回指定索引处的元素
        System.out.println(linkedList.get(2));//数据结构

        //用 element 替换索引 index 处的元素,返回原值
        System.out.println(linkedList.set(0, "Java成神之路"));//Java之旅
        System.out.println(linkedList);//[Java成神之路, JavaSE, 数据结构, Mysql, 算法]

        //检查链表中是否包含至少一个元素 o
        System.out.println(linkedList.contains("数据结构"));//true

        // 返回 o 首次出现的索引,不存在则返回 -1
        System.out.println(linkedList.indexOf("数据结"));//-1
        System.out.println(linkedList.indexOf("数据结构"));//2

        // 返回 o 最后一次出现的索引,不存在则返回 -1
        System.out.println(linkedList);//[Java成神之路, JavaSE, 数据结构, Mysql, 算法]
        System.out.println(linkedList.lastIndexOf("数据结构"));//2
        linkedList.add(3,"数据结构");
        System.out.println(linkedList);//[Java成神之路, JavaSE, 数据结构, 数据结构, Mysql, 算法]
        System.out.println(linkedList.lastIndexOf("数据结构"));//3
        System.out.println(linkedList.lastIndexOf("数控技术"));//-1

        //返回索引从 from 到 to (左闭右开) 的视图
        List<String> temp = linkedList.subList(1,4);
        System.out.println(temp);//[JavaSE, 数据结构, 数据结构]
        temp.remove("数据结构");
        System.out.println(temp);//[JavaSE, 数据结构]
        System.out.println(linkedList);//[Java成神之路, JavaSE, 数据结构, Mysql, 算法]  对该视图的修改会反映在原列表中
        
        LinkedList<String> linkedList2 = new LinkedList<>();
        //将集合 c 中的所有元素按顺序追加到末尾
        linkedList2.addAll(linkedList);
        System.out.println(linkedList2);//[Java成神之路, JavaSE, 数据结构, Mysql, 算法]

        //移除链表中的所有元素
        linkedList2.clear();
        System.out.println(linkedList2);//[]

    }
}

2.4 LinkedList 的遍历方式

java 复制代码
public class Test {
    public static void main(String[] args) {
        LinkedList<Integer> linkedList = new LinkedList<>();
        linkedList.add(1);
        linkedList.add(2);
        linkedList.add(3);
        linkedList.add(4);

        // 1. foreach 遍历
        for (int x : linkedList) {
            System.out.print(x + " ");
        }
        System.out.println(); // 1 2 3 4

        // 2. ListIterator迭代器正向遍历
        ListIterator<Integer> list = linkedList.listIterator();
        while(list.hasNext()) {
            System.out.print(list.next() + " ");//1 2 3 4
        }
        System.out.println();

        // 3. ListIterator迭代器反向遍历
        while(list.hasPrevious()) {
            System.out.print(list.previous() + " ");//4 3 2 1
        }
        System.out.println(); 
        
        // 4.for循环
        for (int i = 0; i < linkedList.size(); i++) {
            System.out.print(linkedList.get(i) + " ");//1 2 3 4
        }
        System.out.println();

        // 5.Iterator迭代器遍历
        Iterator<Integer> it = linkedList.iterator();
        while(it.hasNext()) {
            System.out.print(it.next() + " ");//1 2 3 4 
        }
        System.out.println();
    }
}

2.5 ArrayList 与 LinkedList 的区别

对比维度 ArrayList LinkedList
存储空间 物理上一定连续 物理上不一定连续
随机访问 支持 不支持
头插操作 需搬移所有元素,时间复杂度O(n) 需修改节点引用,时间复杂度为O(1)
插入操作 空间不足时需扩容(1.5倍扩容),可能浪费内存 无扩容需求,插入时直接创建节点
尾插操作 若无需扩容,时间复杂度为O(1);若需扩容,时间复杂度O(n) 时间复杂度为O(1)
删除操作 需搬移后续元素,时间复杂度O(n) 按索引删除 remove(int index):时间复杂度O(n) ; 按引用删除:时间复杂度 O(1)
应用场景 元素存储稳定,频繁进行查询操作 元素频繁进行任意位置插入/删除操作
相关推荐
kylezhao20191 小时前
C#中的反射是什么?详细讲解以及在工控上位机中如何应用
java·开发语言
张三_02261 小时前
Java并发:我用修仙小说讲AQS
java
what丶k1 小时前
【微服务】Spring AI 使用详解:让微服务无缝集成 AI 能力
java·后端·ai编程
骑猪上高速z1 小时前
Easy Desensitize:Java 高性能脱敏引擎的试用与实测
java
工业甲酰苯胺1 小时前
一文学习 Spring AOP 源码全过程
java·学习·spring
知我Deja_Vu1 小时前
详解 Lists.newArrayList() 的作用
java·开发语言
nxb5561 小时前
云原生 tomcat实验设定
java·tomcat
NGC_66111 小时前
归并排序算法
java·数据结构·算法
Andy Dennis2 小时前
Java语法注意事项
java·开发语言·jvm