【数据结构】链表与LinkedList

news2025/4/6 16:14:54

作者主页:paper jie 的博客

本文作者:大家好,我是paper jie,感谢你阅读本文,欢迎一建三连哦。

本文录入于《JAVA数据结构》专栏,本专栏是针对于大学生,编程小白精心打造的。笔者用重金(时间和精力)打造,将javaSE基础知识一网打尽,希望可以帮到读者们哦。

其他专栏:《算法详解》《C语言》《javaSE》等

内容分享:本期将会分享数据结构中的链表知识

目录

链表

链表的概念与结构

单向链表的模拟实现

具体实现代码

MyLinkedList

 indexillgality

LinkedList

LinkedList的模拟实现

MyLinkedList

Indexexception

java中的LinkedList

LinkedList的使用

LinkedList的多种遍历

ArrayList与LinkedList的区别


链表

链表的概念与结构

链表是一种物理存储结构上非连续的存储结构,数据元素的逻辑顺序是通过链表中的引用链接次序实现的。大家可以把它理解为现实中的绿皮火车

这里要注意:

链式在逻辑上是连续的,但是在物理上不一定是连续的

现实中的结点一般都是从堆上申请出来的

从堆上申请的空间,是按照一定的策略来分配的,所以两次申请的空间可能连续,也可能不连续

链表中的结构是多样的,根据情况来使用,一般使用一下结构:

单向或双向

带头和不带头

循环和非循环

这些结构中,我们需要重点掌握两种:

无头单向非循环链表:结构简单,一般不会单独来存数据,实际上更多的是作为其他数据结构的子结构,如哈希桶,图的邻接表等。

无头双向链表:在我们java的集合框架中LinkedList低层实现的就是无头双向循环链表。

单向链表的模拟实现

下面是单向链表需要实现的一些基本功能:

// 1、无头单向非循环链表实现
public class SingleLinkedList {
//头插法
public void addFirst(int data){
} 
//尾插法
public void addLast(int data){
} 
//任意位置插入,第一个数据节点为0号下标
public void addIndex(int index,int data){
} 
//查找是否包含关键字key是否在单链表当中
public boolean contains(int key){
return false;
} 
//删除第一次出现关键字为key的节点
public void remove(int key){
}
//删除所有值为key的节点
public void removeAllKey(int key){
} 
//得到单链表的长度
public int size(){
return -1;
}
public void clear() {
}
public void display() {}
}

具体实现代码

MyLinkedList
package myLinkedList;

import sun.awt.image.ImageWatched;

import java.util.List;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: sun杰
 * Date: 2023-09-14
 * Time: 10:38
 */
public class MyLinkedList implements IList{

    static class LinkNode {
        public int value;
        public LinkNode next;
        public LinkNode(int data) {
            this.value = data;
        }
    }
    LinkNode head;
    public void createNode() {
        LinkNode linkNode1 = new LinkNode(12);
        LinkNode linkNode2 = new LinkNode(23);
        LinkNode linkNode3 = new LinkNode(34);
        LinkNode linkNode4 = new LinkNode(56);
        LinkNode linkNode5 = new LinkNode(78);
        linkNode1.next = linkNode2;
        linkNode2.next = linkNode3;
        linkNode3.next = linkNode4;
        linkNode4.next = linkNode5;
        this.head = linkNode1;
    }


    @Override
    public void addFirst(int data) {
        //实例化一个节点
        LinkNode firstNode = new LinkNode(data);
        if(this.head == null) {
            this.head = firstNode;
            return;
        }
        //将原第一个对象的地址给新节点的next,也就是将head给新next
        firstNode.next = this.head;
        //将新的对象的地址给head头
        this.head = firstNode;

    }

    @Override
    public void addLast(int data) {
        //实例化一个节点
        LinkNode lastNode = new LinkNode(data);
        //找到最后一个节点
        LinkNode cur = this.head;
        while(cur.next!= null) {
            cur = cur.next;
        }
        cur.next = lastNode;
        //将最后一个节点的next记录插入节点的地址
    }

    @Override
    public void addIndex(int index, int data) throws indexillgality {
        if(index < 0 || index > size()) {
            throw new indexillgality("index不合法");
        }
        LinkNode linkNode = new LinkNode(data);
        if(this.head == null) {
            addFirst(data);
            return;
        }
        if(size() == index ) {
            addLast(data);
            return;
        }
        LinkNode cur = this.head;
        int count = 0;
        while(count != index - 1) {
            cur = cur.next;
            count++;
        }
        linkNode.next = cur.next;
        cur.next = linkNode;
    }

    @Override
    public boolean contains(int key) {
        LinkNode cur = this.head;
        while(cur != null) {
            if(cur.value == key) {
                return true;
            }
            cur = cur.next;
        }
        return false;

    }

    @Override
    public void remove(int key) {
        if(this.head.value == key) {
            this.head = this.head.next;
            return ;
        }
        //找前驱
        LinkNode cur = findprev(key);
        //判断返回值
        if(cur != null) {
            //删除
            LinkNode del = cur.next;
            cur.next = del.next;
            //cur.next = cur.next.next;
        }
    }
    //找删除的前驱
    private LinkNode findprev(int key) {
        LinkNode cur = head;
            while(cur.next != null) {
                if(cur.next.value == key) {
                    return cur;
                }
                cur = cur.next;
            }
        return null;
    }

    @Override
    public void removeAllKey(int key) {
        if(size() == 0) {
            return ;
        }
        if(head.value == key) {
            head = head.next;
        }
        LinkNode cur = head.next;
        LinkNode prev = head;
        while(cur != null) {
            if(cur.value == key) {
                prev.next = cur.next;
            }
            prev = cur;
            cur = cur.next;
        }

    }

    @Override
    public int size() {
        LinkNode cur = head;
        int count = 0;
        while(cur != null) {
            count++;
            cur = cur.next;
        }
        return count;
    }

    @Override
    public void display() {
        LinkNode x = head;
        while(x != null) {
            System.out.print(x.value + " ");
            x = x.next;
        }
        System.out.println();
    }

    @Override
    public void clear() {
        LinkNode cur = head;
        while(cur != null) {
            LinkNode curNext = cur.next;
            cur.next = null;
            cur = curNext;
        }
        head = null;
    }
}
 indexillgality

这时一个自定义异常

package myLinkedList;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: sun杰
 * Date: 2023-09-14
 * Time: 12:55
 */
public class indexillgality extends RuntimeException {
    public indexillgality(String message) {
        super(message);
    }
}

LinkedList

LinkedList的模拟实现

这相当于无头双向链表的实现,下面是它需要的基本功能:

// 2、无头双向链表实现
public class MyLinkedList {
//头插法
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 display(){}
public void clear(){}
}

MyLinkedList

package myLinkedList;

import java.util.List;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: sun杰
 * Date: 2023-09-20
 * Time: 18:49
 */
public class MyLinkedList implements IList {

    //单个节点
    public static class ListNode {
        private int val;
        private ListNode prev;
        private ListNode next;

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

    ListNode head;
    ListNode last;
    @Override
    public void addFirst(int data) {
        ListNode cur = new ListNode(data);
        if(head == null) {
            cur.next = head;
            head = cur;
            last = cur;
        }else {
            cur.next = head;
            head.prev = cur;
            head = cur;
        }

    }

    @Override
    public void addLast(int data) {
        ListNode cur = new ListNode(data);
        if(head == null) {
            head = cur;
            last = cur;
        } else {
            last.next = cur;
            cur.prev = last;
            last = cur;
        }
    }

    @Override
    public void addIndex(int index, int data) throws Indexexception {
        ListNode cur = new ListNode(data);
        if(index < 0 || index > size()) {
            throw new Indexexception("下标越界");
        }

        //数组为空时
        if(head == null) {
            head = cur;
            last = cur;
            return ;
        }

        //数组只有一个节点的时候
        if(head.next == null || index == 0) {
            head.prev = cur;
            cur.next = head;
            head = cur;
            return;
        }

        if(index == size()) {
            last.next = cur;
            cur.prev = last;
            return ;
        }

        //找到对应下标的节点
        ListNode x = head;
        while(index != 0) {
            x = x.next;
            index--;
        }

        //头插法
        cur.next = x;
        cur.prev = x.prev;
        x.prev.next = cur;
        x.prev = cur;

    }

    @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) {
        if(head == null) {
            return;
        }
        ListNode cur = head;
        while(cur != null) {
            if(cur.val == key) {
                if(cur.next == null && cur.prev == null) {
                    head = null;
                    last = null;
                    return;
                }else if(cur.next == null){
                    cur.prev.next = null;
                    last = cur.prev;
                    return;
                }else if(cur.prev == null) {
                    head = cur.next;
                    cur.next.prev = null;
                    return ;
                }else {
                    ListNode frone = cur.prev;
                    ListNode curnext = cur.next;
                    frone.next = curnext;
                    curnext.prev = frone;
                    return ;
                }

            }
            cur = cur.next;
        }

    }

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

                }else {
                    ListNode frone = cur.prev;
                    ListNode curnext = cur.next;
                    frone.next = curnext;
                    curnext.prev = frone;
                }
            }
            cur = cur.next;
        }

    }

    @Override
    public int size() {
        int count = 0;
        ListNode cur = head;
        while(cur != null) {
            count++;
            cur = cur.next;
        }
        return count;
    }

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

    @Override
    public void clear() {
        if(head == null) {
            return;
        }
        ListNode cur = head.next;
        while(cur != null) {
            head = null;
            head = cur;
            cur = cur.next;
        }
        head = null;
    }
}

Indexexception

这也是一个自定义异常

package myLinkedList;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: sun杰
 * Date: 2023-09-21
 * Time: 9:47
 */
public class Indexexception extends RuntimeException{
    public Indexexception(String message) {
        super(message);
    }
}

java中的LinkedList

LinkedList的底层是双向链表结构,由于链表没有将元素存储在连续的空间中,元素存储在单独的节点中,然后通过引用将节点连接起来。因为这样,在任意位置插入和删除元素时,是不需要搬移元素,效率比较高。 

在集合框架中,LinkedList也实现了List接口:

注意:

LinkedList实现了List接口

LinkedList的底层使用的是双向链表

Linked没有实现RandomAccess接口,因此LinkedList不支持随机访问

LinkedList的随机位置插入和删除元素时效率较高,复杂度为O(1)

LinkedList比较适合任意位置插入的场景

LinkedList的使用

LinkedList的构造:

一般来说有两种方法:

无参构造:

List<Integer> list = new LinkedList<>();

使用其他集合容器中的元素构造List:

public LinkedList(Collection<? extends E> c)

栗子:

public static void main(String[] args) {
// 构造一个空的LinkedList
        List<Integer> list1 = new LinkedList<>();
        List<String> list2 = new java.util.ArrayList<>();
        list2.add("JavaSE");
        list2.add("JavaWeb");
        list2.add("JavaEE");
// 使用ArrayList构造LinkedList
        List<String> list3 = new LinkedList<>(list2);
    }

LinkedList的基本方法:

public static void main(String[] args) {
LinkedList<Integer> list = new LinkedList<>();
list.add(1); // add(elem): 表示尾插
list.add(2);
list.add(3);
list.add(4);
list.add(5);
list.add(6);
list.add(7);
System.out.println(list.size());
System.out.println(list);
// 在起始位置插入0
list.add(0, 0); // add(index, elem): 在index位置插入元素elem
System.out.println(list);
list.remove(); // remove(): 删除第一个元素,内部调用的是removeFirst()
list.removeFirst(); // removeFirst(): 删除第一个元素
list.removeLast(); // removeLast(): 删除最后元素
list.remove(1); // remove(index): 删除index位置的元素
System.out.println(list);
// contains(elem): 检测elem元素是否存在,如果存在返回true,否则返回false
if(!list.contains(1)){
list.add(0, 1);
}
list.add(1);
System.out.println(list);
System.out.println(list.indexOf(1)); // indexOf(elem): 从前往后找到第一个elem的位置
System.out.println(list.lastIndexOf(1)); // lastIndexOf(elem): 从后往前找第一个1的位置
int elem = list.get(0); // get(index): 获取指定位置元素
list.set(0, 100); // set(index, elem): 将index位置的元素设置为elem
System.out.println(list);
// subList(from, to): 用list中[from, to)之间的元素构造一个新的LinkedList返回
List<Integer> copy = list.subList(0, 3);
System.out.println(list);
System.out.println(copy);
list.clear(); // 将list中元素清空
System.out.println(list.size());
}

LinkedList的多种遍历

foreach:

public static void main(String[] args) {
        List<Integer> list = new LinkedList<>();
        list.add(1);
        list.add(3);
        list.add(5);
        list.add(2);
        list.remove(1);
        for (int x:list) {
            System.out.print(x + " ");
        }
    }

使用迭代器遍历:

ListIterator<Integer> it = list.listIterator();
        while(it.hasNext()) {
            System.out.println(it.next() + " ");
        }
    }

ArrayList与LinkedList的区别


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1054960.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Nodejs搭建本地http服务器,通过【内网穿透】实现远程访问

目录 前言 1.Node.js下载 2.创建node服务器 3.打开node服务器 4.内网穿透实现远程访问 4.1安装cpolar 4.2创建隧道&#xff08;网站&#xff09; 4.3固定公网地址 前言 今天我们学习一个有意思的东西&#xff0c;那就是内网穿透。在此之前我有一些关于html、CSS和Java…

c++---I/o操作

5、文件操作 程序运行时产生的数据都属于临时数据&#xff0c;程序一旦运行结束都会被释放。 我们可以通过文件将数据持久化 C中对文件操作需要包含头文件 <fstream> 文件类型分为两种&#xff1a; 文本文件 - 文件以文本的ASCII码形式存储在计算机中二进制文件 - 文…

机器学习之SGD, Batch, and Mini Batch的简单介绍

文章目录 总述SGD(Stochastic Gradient Descent)(随机梯度下降&#xff09;Batch &#xff08;批量&#xff09;mini Batch (迷你批量&#xff09; 总述 SGD, Batch, and Mini Batch是可用于神经网络的监督学习计算权重更新的方案&#xff0c;即∆wij。 SGD(Stochastic Gradi…

三等分功分器[波导]设计详细教程

想必大家通过阅读相关文献可以发现三等分实现可以有很多不同的方法&#xff0c;这里采用的是先不等分再等分的方式&#xff0c;仅供参考。 主要指标 中心频率为280GHz&#xff0c;采用WR-3频段的标准波导&#xff0c;将2:1不等功率分配耦合器与3dB等功率分配耦合器级联&#…

JUC——并发编程—第三部分

四大函数式接口(必须掌握) 函数式接口:只有一个方法的接口 FunctionalInterface public interface Runnable {public abstract void run(); }//简化编程模型&#xff0c;在新版本的框架底层大量应用 //foreach(消费者的函数式接口)Function接口(函数型接口) /*** Function 函…

中睿天下参展2023海军工程大学首届网络安全文化周并发表主题演讲

2023年9月3日至9月8日&#xff0c;海军工程大学首届网络安全文化周活动于武汉举办。本次活动以“守护蓝疆网安有我”为主题&#xff0c;设有特邀嘉宾前沿讲座、网络安全圆桌交流论坛、网络安全科技展、网络对抗实战竞技、网络安全保密视频创作和信息安全知识竞赛等系列活动。 海…

聊聊网络编程中的粘包、拆包、半包、编解码

聊聊网络编程中的粘包、拆包、半包、编解码 文章目录 1. 引言2. 粘包、拆包、半包现象解析2.1. 粘包现象2.2. 拆包现象2.3. 半包现象 3. 解决粘包、拆包、半包问题的方法4. netty 是如何解决解决粘包、拆包、半包问题参考文档 1. 引言 在网络编程中&#xff0c;TCP/IP 协议是…

uboot启动流程-涉及s_init汇编函数

一. uboot启动涉及函数 本文简单分析uboot启动流程中&#xff0c;涉及的汇编函数&#xff1a; lowlevel_init函数调用的函数&#xff1a;s_init 函数 save_boot_params_ret函数调用的函数&#xff1a; _main 函数 本文继上一篇文章的学习&#xff0c;地址如下&#xff1a;…

第七章 查找 八、B树

目录 一、定义 二、B树的核心特性 1、B树各个结点的子树数和关键字数 2、子树高度 3、关键字的值 4、B树高度 三、B树的插入 四、B树的删除 一、定义 B树&#xff0c;又称多路平衡查找树&#xff0c;B树中所有结点的孩子个数的最大值称为B树的阶&#xff0c;通常用m表示…

Apache Commons Pool2 池化技术

对象池是一种设计模式&#xff0c;用于管理和重用对象&#xff0c;以提高性能和资源利用率。对象池的概念在许多应用程序中都有广泛应用&#xff0c;特别是在需要频繁创建和销毁对象的情况下&#xff0c;例如数据库连接、线程、HTTP连接等 对象池通过预先创建一组对象并将它们存…

MPLS虚拟专用网--跨域OptionC方案

OptionC方案 前面介绍的两种方式都能够满足跨域VPN的组网需求,但这两种方式也都需要ASBR参与VPN-IPv4路由的维护和发布。当每个AS都有大量的VPN路由需要交换时,ASBR就很可能阻碍网络进一步的扩展。 解决上述问题的方案是:ASBR不维护或发布VPN-IPv4路由,PE之间直接交换VPN-…

【算法】算法基础课模板大全

一、基础算法 快速排序算法模板 void quick_sort(int q[], int l, int r) {//递归的终止情况if (l > r) return;//选取分界线。这里选数组中间那个数int i l - 1, j r 1, x q[l r >> 1];//划分成左右两个部分while (i < j){do i ; while (q[i] < x);do …

用两个栈来实现队列

typedef int SltDatatype; typedef struct Stack {SltDatatype* a;//开辟栈的动态内存空间int top;//记录栈顶int capacity;//记录容量 }ST;void STInit(ST* ps);//栈的初始化 void STDestroy(ST* ps);//释放 void STPush(ST* ps, SltDatatype x);//入栈 void STPop(ST* ps);//…

喝健康白酒 有益生心健康

中国的制酒史源远流长&#xff0c;酒渗透在中华五千年的文化中。酒与烟不同&#xff0c;烟对人体有百害而无一利&#xff0c;而对于酒&#xff0c;若掌握好饮酒的度&#xff0c;对人体有一定的养生作用&#xff0c;所以我们通常会说“戒烟限酒”。 据一些专家研究&#xff0c;…

下载盗版网站视频并将.ts视频文件合并

. 1.分析视频请求123 2.数据获取和拼接 1.分析视频请求 1 通过抓包观察我们发现视频是由.ts文件拼接成的每一个.ts文件代表一小段2 通过观察0.ts和1.ts的url我们发现他们只有最后一段不同我们网上找到url获取的包3 我们发现index.m3u8中储存着所有的.ts文件名在拼接上前面固定…

重置Jetson设备的Ubuntu密码:通过挂载根目录到另一个Linux系统

在本文中&#xff0c;我们将介绍如何在忘记Ubuntu 20.04密码的情况下重置密码。我们将通过将Ubuntu的根目录挂载到另一个Linux系统来实现这一目的。我们还将介绍chroot命令的功能。 1. 背景 最近&#xff0c;我们研发团队遇到了一个棘手的问题。一台用于研发&#xff0c;多人使…

验证曲线(validation_curve)项目实战

验证曲线 validation_curve 一、简介 validation_curve验证曲线&#xff0c;可确定不同参数值下的训练和测试分数 根据指定参数的不同值计算估计器的得分 这与使用一个参数的网格搜索类似。不过&#xff0c;这也会计算训练得分&#xff0c;只是一个用于绘制结果的工具。 二、…

十个有用的 Vue.js 自定义 Hook

Vue.js 是我使用的第一个 JavaScript 框架。 我可以说 Vue.js 是我进入 JavaScript 世界的第一扇门之一。 目前&#xff0c;Vue.js 仍然是一个很棒的框架。 我认为有了组合 API&#xff0c;Vue.js 只会增长得更多。 在本文中&#xff0c;我将向分享 10 个可以使用 Vue.js 制作…

计算机竞赛 深度学习手势识别 - yolo python opencv cnn 机器视觉

文章目录 0 前言1 课题背景2 卷积神经网络2.1卷积层2.2 池化层2.3 激活函数2.4 全连接层2.5 使用tensorflow中keras模块实现卷积神经网络 3 YOLOV53.1 网络架构图3.2 输入端3.3 基准网络3.4 Neck网络3.5 Head输出层 4 数据集准备4.1 数据标注简介4.2 数据保存 5 模型训练5.1 修…

竞赛 多目标跟踪算法 实时检测 - opencv 深度学习 机器视觉

文章目录 0 前言2 先上成果3 多目标跟踪的两种方法3.1 方法13.2 方法2 4 Tracking By Detecting的跟踪过程4.1 存在的问题4.2 基于轨迹预测的跟踪方式 5 训练代码6 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习多目标跟踪 …