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)
应用场景 元素存储稳定,频繁进行查询操作 元素频繁进行任意位置插入/删除操作
相关推荐
xcl09252 小时前
北京24小时自助健身房系统软件开发实战:从架构到部署全流程指南
java·spring boot·架构
一 乐4 小时前
动漫书销售商城|基于springboot + vue动漫书销售商城(源码+数据库+文档)
java·数据库·vue.js·spring boot·毕业设计
卓怡学长4 小时前
w214基于jsp知道特产网
java·intellij-idea
步行cgn4 小时前
Spring 注解使用详解
java·spring
一条小小yu4 小时前
Spring IoC的理解
java·后端·spring
落魄实习生5 小时前
Agent Scope Java 2.x 系列【7】工具使用
java·开发语言·ai
旺仔学长 哈哈5 小时前
springboot钓鱼爱好者交流平台APP设计与实现
java·spring boot·mysql·充电桩管理系统
自强的小白6 小时前
核心功能(Service接口)
java·mybatis
乌暮7 小时前
深入理解 Java 泛型:把「万能盒子」用对、用稳
java·开发语言·后端·学习