Java——LinkedList

1、链表

1.1 链表的概念及结构

链表在逻辑层面上是连续的,在物理层面上不一定是连续的

链表结构可分为,单向或双向、带头或不带头、循环或非循环,组合共计8种

重点:无头单向非循环链表、无头双向链表

1.2 模拟实现无头单向非循环链表

一个链表由若干节点组成,结合 内部类 知识,可将节点类定义在链表类中,成为内部类

java 复制代码
public class MySingleLinkedList {

    static class ListNode {//该内部类中定义的是节点的属性
        public int val;
        public ListNode next;

        public ListNode(int val) {
            this.val = val;
        }
    }

    public ListNode head;//链表的头节点,定义在MySingleLinkedList类中
    
}

下面以穷举的方式创建链表方便理解(真正创建链表并非如此)

java 复制代码
//MySingleLinkedList类

    //该方法创建一个链表,头节点为node1,当该方法结束后,变量node1...都会销毁,只剩下头节点为head的链表
    public void createdList(){
        ListNode node1 = new ListNode(1);
        ListNode node2 = new ListNode(2);
        ListNode node3 = new ListNode(3);
        ListNode node4 = new ListNode(4);
        ListNode node5 = new ListNode(5);

        node1.next = node2;
        node2.next = node3;
        node3.next = node4;
        node4.next = node5;

        this.head = node1;
    }

打印链表

java 复制代码
//MySingleLinkedList 类

    public void display() {
        ListNode cur = head; //若直接使用head进行遍历打印,将只能打印一次,再次调用该函数,头节点就不在原来的位置了
        while(cur != null) { //注意!此处若写成 cur.next != null 则不会打印最后一个节点
            System.out.print(cur.val + " ");
            cur = cur.next;
        }
    }

求链表长度:

java 复制代码
    //遍历即可
    public int size() {
        ListNode cur = head;
        int count = 0;
        while(cur != null) {
            cur = cur.next;
            count++;
        }
        return count;
    }

头插法:

java 复制代码
    public void addFirst(int data) {
        ListNode newNode = new ListNode(data);
        newNode.next = head;
        head = newNode;
    }

尾插法:

java 复制代码
    public void addLast(int data) {
        ListNode newNode = new ListNode(data);
        //不要忘记:判空!
        if(head == null) {
            head = newNode;
            return;
        }
        ListNode cur = head;
        while(cur.next != null) { //此处若写成 cur.next != null 则不会打印最后一个节点
            cur = cur.next;
        }
        cur.next = newNode;
    }

在index位置插入

java 复制代码
// IndexNotLegalException 异常类
public class IndexNotLegalException extends RuntimeException{
    public IndexNotLegalException() {

    }
    public IndexNotLegalException(String msg) {
        super(msg);
    }
}


// MySingleLinkedList 类
    public void addIndex(int index, int data) {
        //1、判断index合法性
        try{
            checkIndex(index);
        }catch(IndexNotLegalException e) {
            e.printStackTrace();
        }
        //2、index == 0 || index == size()
        if(index == 0) {
            addFirst(data);
            return;
        }
        if(index == size()) {
            addLast(data);
            return;
        }
        //3、找到index的前一个位置
        ListNode cur = findIndexSubOne(index);
        //4、进行连接
        ListNode node = new ListNode(data);
        node.next = cur.next;
        cur.next = node;
    }
    private void checkIndex(int index) throws IndexNotLegalException{
        if(index < 0 || index > size()) {
            throw new IndexNotLegalException("index不合法!");
        }
    }
    private ListNode findIndexSubOne(int index) {
        int count = 0;
        ListNode cur = head;
        while(count < index-1) {
            cur = cur.next;
            count++;
        }
        return cur;
    }

查找关键字key是否包含在链表中

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

删除第一次出现关键字key的节点

java 复制代码
    public void remove(int key) {
        //判空
        if(head == null) {
            return;
        }
        //若头节点为key
        while(head.val == key) {
            head = head.next;
            return;
        }
        ListNode cur = head;
        //遍历找到值为key的节点
        while(cur.next != null) {
            if(cur.next.val == key) {
                cur.next = cur.next.next;
                return;
            }
            cur = cur.next;
        }
        System.out.println("没有找到要删除的数字!");
    }

删除所有值为key的节点

java 复制代码
    public void removeAllKey(int key) {
        //判空
        if (this.head == null) {
            return;
        }
        //快慢指针
        ListNode prev = head;
        ListNode cur = head.next;
        while(cur != null) {
            if(cur.val == key) {
                prev.next = cur.next;
            }else {
                prev = cur;
            }
            cur = cur.next;
        }
        //处理头节点,当上述操作完成后,只剩下头节点没有判断
        //该方法优于一上来就判断头节点
        if(head.val == key) {
            head =head.next;
        }
    }

清空链表

java 复制代码
    public void clear() {
        ListNode cur = head;
        while(cur != null) {
            ListNode curN = cur.next;
            cur.next = null;
            cur = curN;
        }
        head = null;
    }

1.3 模拟实现无头双向非循环链表

java 复制代码
public class MyLinkedList {
    static class ListNode {
        public int data;
        public ListNode prev;//前驱节点
        public ListNode next;//后继节点

        public ListNode(int data){
            this.data = data;
        }
    }

    public ListNode head;//头节点
    public ListNode last;//尾节点

    //得到单链表的长度
    public int size(){
        int count = 0;
        ListNode cur = head;
        while(cur != null) {
            count++;
            cur = cur.next;
        }
        return count;
    }
    public void display(){
        ListNode cur = head;
        while(cur != null) {
            System.out.print(cur.data + " ");
            cur = cur.next;
        }
        System.out.println();
    }
    //查找是否包含关键字key是否在单链表当中
    public boolean contains(int key){
        ListNode cur = head;
        while(cur != null) {
            if(cur.data == key) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }
    //头插法
    public void addFirst(int data){
        ListNode node = new ListNode(data);
        if(head == null) {
            head = last = node;
        }else {
            head.prev = node;
            node.next = head;
            head = node;
        }
    }
    //尾插法
    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;
        }
    }
    //任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index,int data){
        try{
            checkIndex(index);
        }catch(IndexIllegalException e) {
            e.printStackTrace();
        }
        if(index == 0) {
            addFirst(data);
            return;
        }
        if(index == size()) {
            addLast(data);
            return;
        }
        ListNode node = new ListNode(data);
        ListNode cur = findIndex(index);
        node.next = cur;
        node.prev = cur.prev;
        cur.prev.next = node;
        cur.prev = node;
    }
    private ListNode findIndex(int index) {
        ListNode cur = head;
        while(index != 0) {
            cur = cur.next;
            index--;
        }
        return cur;
    }
    private void checkIndex(int index) throws IndexIllegalException{
        if(index < 0 || index > size()) {
            throw new IndexIllegalException("双向链表index不合法!");
        }
    }

    //删除第一次出现关键字为key的节点
    public void remove(int key){
        ListNode cur = head;
        while(cur != null) {
            if(cur.data == key) {
                if(cur == head) {
                    head = head.next;
                    if(head != null) {
                        head.prev = null;
                    }else {
                        last = null;
                    }
                }else if(cur == last) {
                    cur.prev.next = null;
                    last = cur.prev;
                }else {
                    cur.prev.next = cur.next;
                    cur.next.prev = cur.prev;
                }
                return;

            }
            cur = cur.next;
        }
    }
    //删除所有值为key的节点
    public void removeAllKey(int key){
        ListNode cur = head;
        while(cur != null) {
            if(cur.data == key) {
                if(cur == head) {
                    head = head.next;
                    if(head != null) {
                        head.prev = null;
                    }else {
                        last = null;
                    }
                }else if(cur == last) {
                    cur.prev.next = null;
                    last = cur.prev;
                }else {
                    cur.prev.next = cur.next;
                    cur.next.prev = cur.prev;
                }
            }
            cur = cur.next;
        }
    }

    public void clear(){
        ListNode cur = head.next;
        while(cur != null) {
            cur = head.next;
            head.prev = null;
            head.next = null;
            head = cur;
        }
        head = last = null;
    }
}

链表遍历方式:

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

        //直接sout打印
        System.out.println(list);
        System.out.println("======");

        //foreatch循环打印
        for(Integer x : list) {
            System.out.print(x + " ");
        }
        System.out.println();
        System.out.println("======");

        //for循环打印
        for (int i = 0; i < list.size(); i++) {
            System.out.print(list.get(i) + " ");
        }
        System.out.println();
        System.out.println("======");

        //Iterator打印
        Iterator<Integer> it = list.iterator();
        while(it.hasNext()) {
            System.out.print(it.next() + " ");
        }
        System.out.println();
        System.out.println("======");

        //ListIterator可以倒着打印
        ListIterator<Integer> it3 = list.listIterator(list.size());
        while(it3.hasPrevious()) {
            System.out.print(it3.previous() + " ");
        }
    }

运行结果:

2、ArrayList 和 LinkedList 的区别

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

相关推荐
Scott9999HH1 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
腻害兔1 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
码智社2 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海2 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖3 小时前
JDK 26 新特性详解
java·开发语言
马优晨4 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区6 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大6 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai7 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
维天说7 小时前
CLI-Switch 2026年3月版历史设计:Hook、TTY 隔离与 JSON 状态
java·服务器·json