[数据结构] LinkedList

1. 什么是 LinkedList

LinkedList (官方文档)

LinkedList是Java集合框架中的一个双向链表实现,位于java.util包中。它实现了ListDeque接口,支持高效的插入和删除操作,但随机访问性能较差


2. LinkedList 的 特性

  • 双向链表结构 :每个节点 (Node)包含前驱(prev)、后继(next)和当前元素(item)的引用
  • 非连续内存存储 :与ArrayList不同,LinkedList的元素在内存中不连续存储
  • 动态大小 :无需预先分配空间,大小随元素增减动态变化

3. LinkedList 实现的接口

  • LinkedList 实现了 List 接口
  • LinkedList 的底层是使用了双向链表
  • LinkedList 没有实现 RandomAccess 接口 , 因此 LinkedList 不支持随机访问
  • LinkedList 的任意插入和删除元素时效率比较高效 , 时间复杂度为O(1)
  • LinkedList 比较适合任意位置插入的场景

4. LinkedList 的使用

4.1 LinkedList 的创建

① LinkedList 的创建

java 复制代码
    public static void main(String[] args) {
        List<Integer> list1 = new LinkedList<>();
        list1.add(1);
        list1.add(2);
        list1.add(3);
        System.out.println(list1);
    }

② 用 ArrayList 来构造 LinkedList

java 复制代码
    public static void main(String[] args) {
        List<Integer> list2 = new java.util.ArrayList<>();
        list2.add(22);
        list2.add(122);
        list2.add(233);
        list2.add(344);
        System.out.println(list2);
        List<Integer> list3 = new LinkedList<>(list2);
    }

4.2 LinkedList 常用方法的介绍

|-------------------------------------------------|---------------------------------|
| 方法 | 解释 |
| boolean add (E e) | 尾插 e |
| void add (int index , E element) | 将 e 插入 到 index 位置 |
| boolean addAll (Collection<? extends E> c) | 尾插 c 中的元素 |
| E remove (int index) | 删除 index 位置的元素 |
| boolead remove (Object o) | 删除 遇到的第一个元素 o |
| E get (int index) | 获取 下标 index 的元素 |
| E set (int index , E element) | 将下标 index 位置的元素设置 为 element |
| void clear () | 清空 |
| boolean contains (Object o) | 判断 o 是否包含 在线性表中 |
| int indexOf (Object o) | 返回 第一个 o 所在位置的下标 |
| int lastindexOf (Object o) | 返回 最后一个 o 所在位置的下标 |
| List<E> subList (int fromIndex , int toIndex) | 截取 部分 list |

部分方法演示

java 复制代码
    public static void main(String[] args) {
        LinkedList<Integer> list = new LinkedList<>();
        list.add(11);
        list.add(22);
        list.add(33);
        list.add(44);
        list.add(99);
        list.add(22);
        System.out.println(list);//[11, 22, 33, 44, 99, 22]

        list.add(0,88);//指定位置插入
        System.out.println(list);//[88, 11, 22, 33, 44, 99, 22]

        list.remove(0);//删除指定下标元素
        list.removeFirst();//删除第一个元素
        list.removeLast();//删除最后一个元素
        System.out.println(list);//[22, 33, 44, 99]

        list.set(3,00);//将下标为3的元素设置为0
        System.out.println(list);//[22, 33, 44, 0]

        List<Integer> list1 = list.subList(0,4);//截取list 从[0,4)下标 并复制为新的链表
        System.out.println(list1);//[22, 33, 44, 0]

        list.clear();//清空链表
        System.out.println(list);//[]
    }

4.3 LinkedList 的遍历

注意 : 以下代码省略了 main 方法

java 复制代码
        LinkedList<Integer> list = new LinkedList<>();
        list.add(11);
        list.add(22);
        list.add(33);
        list.add(44);
        list.add(99);
        list.add(22);

① for each 遍历

java 复制代码
        //for each 遍历
        for (int e:list) {
            System.out.print(e+" ");//11 22 33 44 99 22
        }
        System.out.println();

② for 循环遍历

java 复制代码
        //for 循环遍历
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i)+" ");//11 22 33 44 99 22
        }
        System.out.println();

③ 迭代器遍历(正向遍历)

java 复制代码
        //迭代器遍历(正向遍历)
        ListIterator<Integer> it = list.listIterator();
        while (it.hasNext()){
            System.out.print(it.next()+" ");//11 22 33 44 99 22
        }
        System.out.println();

④ 迭代器遍历(方向遍历)

java 复制代码
        ListIterator<Integer> it1 = list.listIterator(list.size());
        while (it1.hasPrevious()){
            System.out.print(it1.previous()+" ");//22 99 44 33 22 11
        }
        System.out.println();

5. LinkedList 与 ArrayList 的对比

5.1 场景选择

场景 推荐使用 原因
频繁随机访问 ArrayList 基于数组的索引访问效率更高(O (1))
频繁插入删除 LinkedList 链表结构修改引用即可,无需移动元素
内存敏感场景 ArrayList 链表节点有额外内存开销
作为队列使用 LinkedList 实现了 Deque 接口,队列操作更便捷

5.2 区别

不同点 ArrayList LinkedList
存储空间上 物理上一定连续 逻辑上连续,但物理上不一定连续
随机访问 支持,时间复杂度 O (1) 不支持,时间复杂度 O (N)
头插 需要搬移元素,效率低,时间复杂度 O (N) 只需修改引用的指向,时间复杂度 O (1)
插入 空间不够时需要扩容 没有容量的概念
应用场景 元素高效存储 + 频繁访问 任意位置插入和删除频繁

6. LinkedList 的模拟实现

①接口

java 复制代码
public interface IList {
    //头插法
    public void addFirst(int data);
    //尾插法
    public void addLast(int data);
    //任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index,int data);
    //查找是否包含关键字key是否在单链表当中
    public boolean contains(int key);
    //删除第一次出现关键字为key的节点
    public void remove(int key);
    //删除所有值为key的节点
    public void removeAllKey(int key);
    //得到单链表的长度
    public int size();
    public void clear();
    public void display();
}

②具体方法的实现

java 复制代码
public class MyLinkedList implements IList  {

    static class ListNode{//内部类
        public ListNode prev;//结点的前驱
        public ListNode next;//结点的后继
        public int val;

        public ListNode(int val){
            this.val = val;
        }
    }
    //类成员
        public ListNode head;//头结点
        public ListNode last;//尾结点

    //头插法
    @Override
    public void addFirst(int data) {
        ListNode node = new ListNode(data);
        if(head == null){
            head = last = node;
        }else{
            node.next = head;
            head.prev = node;
            head = node;
        }
    }

    //尾插法
    @Override
    public void addLast(int data) {
        ListNode node = new ListNode(data);
        if(head == null){
            head = last = node;
        }else{
            last.next = node;
            node.prev = last;
            last = node;
        }
    }

    //寻找下标为 index 的结点
    private ListNode findIndex(int index){
        ListNode cur = head;
        while (index != 0) {
            cur = cur.next;
            index--;
        }
        return cur;
    }

    //任意位置插入结点
    @Override
    public void addIndex(int index, int data) {
        int len = size();
        if(index<0||index>size()){
            return;
        }
        if(index == 0){
            addFirst(data);
            return;
        }
        if(index == len){
            addLast(data);
            return;
        }
        ListNode node = new ListNode(data);
        ListNode cur = findIndex(index);
        cur.prev.next = node;
        node.next = cur;
        node.prev = cur.prev;
        cur.prev = node;
    }

    //查找是否包含 key 在链表中
    @Override
    public boolean contains(int key) {
        ListNode cur = head;
        while (cur!=null){
            if(cur.val == key){
                return true;
            }
            cur = cur.next;
        }
        return false;
    }


    @Override
    public void remove(int key) {
        ListNode cur = head;
        while(cur!=null){
            if(cur.val == key){
                //删除
                if(cur == head){
                    head = head.next;
                    if(head != null) {
                        head.prev = null;
                    }
                }else {
                    cur.prev.next = cur.next;
                    if (cur.next == null) {
                        last = last.prev;
                    } else {
                        cur.next.prev = cur.prev;
                    }
                }
                return;
            }
                cur = cur.next;
        }
    }

    @Override
    public void removeAllKey(int key) {
        ListNode cur = head;
        while(cur!=null){
            if(cur.val == key){
                //删除
                if(cur == head){
                    head = head.next;
                    if(head != null) {
                        head.prev = null;
                    }
                }else {
                    cur.prev.next = cur.next;
                    if (cur.next == null) {
                        last = last.prev;
                    } else {
                        cur.next.prev = cur.prev;
                    }
                }

            }
            cur = cur.next;
        }
    }

    //求链表的长度
    @Override
    public int size() {
        if(head == null){
            return 0;
        }
        ListNode cur = head;
        int count = 0;
        while (cur!=null){
            count++;
            cur = cur.next;
        }
        return count;
    }

    //清空链表
    @Override
    public void clear() {
        ListNode cur = head;
        while (cur!=null){
            ListNode curN = cur.next;
            cur.prev = null;
            cur.next = null;
            cur = curN;
        }
    }

    //打印链表
    @Override
    public void display() {
        ListNode cur = head;
        while (cur!=null){
            System.out.print(cur.val+" ");
            cur = cur.next;
        }
        System.out.println();
    }
}

③测试

java 复制代码
public class Test {
    public static void main(String[] args) {
        MyLinkedList myLinkedList = new MyLinkedList();
        myLinkedList.addFirst(12);//头插法
        myLinkedList.addLast(22);//尾插法
        myLinkedList.addFirst(45);
        myLinkedList.addFirst(22);
        myLinkedList.display();//22 45 12 22 
        
        myLinkedList.addIndex(2,44);//任意位置插入
        myLinkedList.display();//22 45 44 12 22
        
        myLinkedList.remove(45);//删除元素 45
        myLinkedList.display();//22 44 12 22 
        
        myLinkedList.removeAllKey(22);//删除所有元素 22
        myLinkedList.display();//44 12 
    }
}

相关推荐
前端小L34 分钟前
二分查找专题(九):“降维”的魔术!将二维矩阵“拉平”为一维
数据结构·算法
她说人狗殊途1 小时前
时间复杂度(按增长速度从低到高排序)包括以下几类,用于描述算法执行时间随输入规模 n 增长的变化趋势:
数据结构·算法·排序算法
Miraitowa_cheems1 小时前
LeetCode算法日记 - Day 102: 不相交的线
数据结构·算法·leetcode·深度优先·动态规划
野生技术架构师1 小时前
盘一盘Redis的底层数据结构
数据结构·数据库·redis
Miraitowa_cheems1 小时前
LeetCode算法日记 - Day 101: 最长公共子序列
数据结构·算法·leetcode·深度优先·动态规划
北冥湖畔的燕雀1 小时前
std之list
数据结构·c++·list
南方的狮子先生2 小时前
【C++】C++文件读写
java·开发语言·数据结构·c++·算法·1024程序员节
Alex艾力的IT数字空间2 小时前
完整事务性能瓶颈分析案例:支付系统事务雪崩优化
开发语言·数据结构·数据库·分布式·算法·中间件·php
虫师c3 小时前
List vs Set:深入剖析Java两大集合的核心区别与实战应用
java·数据结构·list·set·java集合
橘颂TA3 小时前
【剑斩OFFER】算法的暴力美学——寻找峰值
数据结构·算法·力扣·c/c++