五分钟“手撕”链表

为了提高大家的学习效率,我把代码放开头,供查阅。 

目录

一、链表的实现代码

二、什么是链表

三、链表的分类

四、链表的常见操作

插入

删除 

五、Java自带的LinkedList 

 两个构造方法

 一些常用方法

六、LinkedList的遍历

七、ArrayList和LinkedList的区别  


一、链表的实现代码

//无头单向链表
package demo1;
public class MySingleLinkedList{
    class ListNode{
        public int var;
        public ListNode next;
        public int val;

        public ListNode(int var) {
            this.var = var;
        }
    }
    ListNode head;
    public void creat(){
        ListNode node1=new ListNode(1);
        ListNode node2=new ListNode(1);
        ListNode node3=new ListNode(1);
        ListNode node4=new ListNode(1);

        node1.next=node2;
        node2.next=node3;
        node3.next=node4;
        head=node1;
    }
    //头插法
    public void addFirst(int data) {
        ListNode node=new ListNode(data);
        node.next=head;
        head=node;
    }
    //尾插法
    public void addLast(int data) {
        ListNode node=new ListNode(data);
        if(head==null){
            head=node;
            return;
        }
        ListNode cur=head;
        while (cur.next!=null){
            cur=cur.next;
        }cur.next=node;
    }

    private void IndexCheck(int index)throws IndexNotLegal {
        if(index<0||index>size()){
            throw new IndexNotLegal("index位置不合法");
        }
    }
    //任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index, int data){
        try {
            IndexCheck(index);
        }catch (IndexNotLegal e){
            e.printStackTrace();
            return;
        }
        ListNode node=new ListNode(data);
        ListNode cur=head;
        if(index==0){
            addFirst(data);
            return;
        }if(index==size()){
            addLast(data);
            return;
        }
        //注意这里是index-1
        for (int i = 0; i < index-1; i++) {
            cur= cur.next;
        }
        node.next=cur.next;
        cur.next=node;
    }
    //查找是否包含关键字key是否在单链表当中
    public boolean contains(int key) {
        ListNode cur=head;
        while (cur!=null){
            if(cur.var==key){
                return true;
            }cur=cur.next;
        }return false;
    }
    //删除第一次出现关键字为key的节点
    public void remove(int key) {
        if(head.var==key){
            head=head.next;
            return;
        }if(head==null){
            return;
        }
        ListNode cur=head;
        while (cur.next!=null){
            if(cur.next.var==key){
                cur.next=cur.next.next;
                return;
            }cur=cur.next;
        }
    }
    //删除所有出现的key节点
    public void removeAllKey(int key) {
        if(head==null){
            return;
        }ListNode prev=head;
        ListNode cur=head.next;
        while (cur!=null){
            if(cur.var==key){
                prev.next= cur.next;
                //这里不能写prev=cur
                cur= cur.next;
            }else {
                prev=cur;
                cur= cur.next;
            }
            if(head.var==key){
                head=head.next;
            }
        }

    }
    //得到单链表的长度
    public int size() {
        ListNode cur=head;
        int count=0;
        while (cur!=null){
            count++;
            cur=cur.next;
        }return count;
    }
    //打印链表
    public void display() {
        ListNode cur=head;
        for (int i = 0; i < size(); i++) {
            System.out.print(cur.var+" ");
            cur=cur.next;
        }
        System.out.println();
    }
    //清除链表
    public void clear() {
        head=null;
    }
}

public class IndexNotLegal extends RuntimeException {
    public IndexNotLegal(){

    }public IndexNotLegal(String s){
        super(s);
    }
}











package demo2;
// 无头双向链表
public class MyLinkedList {

    class ListNode {
        public int var;
        ListNode prev;
        ListNode next;

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

    ListNode head;
    ListNode last;

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

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

    private void indexCheck(int index) throws IndexNotLegal {
        if (index < 0 || index > size()) {
            throw new IndexNotLegal("index位置不合法");
        }
    }

    private ListNode findIndexCur(int index) {
        ListNode cur = head;
        while (index != 0) {
            cur = cur.next;
            index--;
        }
        return cur;
    }

    //任意位置插入,第一个数据节点为0号下标
    public void addIndex(int index, int data) {
        try {
            indexCheck(index);
        } catch (IndexNotLegal e) {
            e.printStackTrace();
        }

        if (index == 0) {
            addFirst(data);
            return;
        }
        if (index == size()) {
            addLast(data);
            return;
        }
        ListNode node = new ListNode(data);
        ListNode cur = findIndexCur(index);
        //先把node插进去,再调整node的前一个,最后再调cur,因为需要cur来调整
        node.next=cur;
        cur.prev.next=node;
        node.prev=cur.prev;
        cur.prev=node;


    }

    //查找是否包含关键字key是否在单链表当中
    public boolean contains(int key) {
        ListNode cur = head;
        while (cur != null) {
            if (cur.var == key) {
                return true;
            }
            cur = cur.next;
        }
        return false;
    }

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

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



    //得到单链表的长度
    public int size() {
        int count = 0;
        ListNode cur = head;
        while (cur != null) {
            cur = cur.next;
            count++;
        }
        return count;
    }

    public void display() {
        ListNode cur = head;
        for (int i = 0; i < size(); i++) {
            System.out.print(cur.var + " ");
            cur = cur.next;
        }
        System.out.println();
    }

    public void clear() {
        head = last = null;
    }
}


package demo2;

public class IndexNotLegal extends RuntimeException{
    public IndexNotLegal(){

    }public IndexNotLegal(String s){
        super(s);
    }
}

二、什么是链表

简单来说,像链子一样的数据结构。像火车一节一节车厢一样,每个元素是独立的个体(内部类)。 并且他们在空间里是分散的 

 

为什么分散的还可以找到下一个呢?

答:一个节点里面装着两种东西,一个是值,一个的下一个的地址,这样根据下一个的地址就可以找到下一个了。 

三、链表的分类

常见的链表有三种,但是我这里只介绍两种:无头单链,无头双链。剩下的一种之后再补充。

单链和双链的区别?

答: 一个节点都是一个类。单链装的是值和下个节点双链装的是值和上个节点和下个节点

四、链表的常见操作

插入

怎么插入元素?在链表中很简单! 

单链p下个节点改成n1,n0下个节点改为p。

双链 p下个节点改为n1,n0下个节点改为p(先把p插进去,成一个逆时针),p上个节点改为n0,n1的上个节点改为p(修改原来的两点节点)

注意!还需要考虑一些特殊情况,具体请看代码的实现!

删除 

在链表中删除节点也非常方便,只需改变一个节点的引用(指针)即可。 

单链:n0的下个节点指向n1。

双链:n0下个节点改为n1,n1上个节点改为n0 。

问题来了:为什么p的指向n1可以不删?

答;原因是以及没人指向p了, 如果没人引用(指向)的东西,就会被系统自动回收。 像clear()方法,head不指向首个节点,那么首个节点被回收,同理下面也如此。

五、Java自带的LinkedList 

 两个构造方法

方法解释
LinkedList()无参构造
public LinkedList(Collection c)使用其他集合容器中元素构造List
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);
}

 一些常用方法

方法解释
boolean add(E e)尾插 e
void add(int index, E element)将 e 插入到 index 位置
boolean addAll(Collection c)尾插 c 中的元素
E remove(int index)删除 index 位置元素
boolean 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 subList(int fromIndex, int toIndex)截取部分 list
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的遍历

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());
//for循环遍历
for (int i = 0; i < list.size(); i++) {
    System.out.print(list.get(i)+" ");
}
// foreach遍历
for (int e:list) {
    System.out.print(e + " ");
}
    System.out.println();
// 使用迭代器遍历---正向遍历
    ListIterator<Integer> it = list.listIterator();
while(it.hasNext()){
    System.out.print(it.next()+ " ");
}
    System.out.println();
// 使用反向迭代器---反向遍历
    ListIterator<Integer> rit = list.listIterator(list.size());
while (rit.hasPrevious()){
    System.out.print(rit.previous() +" ");
}
    System.out.println();
}

七、ArrayList和LinkedList的区别  

不同点数组链表
存储方式连续内存空间分散内存空间
容量扩展长度不可变可灵活扩展
内存效率元素占用内存少、但可能浪费空间元素占用内存多
访问元素O(1)O(N)
添加元素O(N)O(1)
删除元素O(N)O(1)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

达梦数据库写文件的方式探索

0x01 前沿 这篇文章整体算是《达梦数据库手工注入笔记》的续集&#xff0c;达梦作为国内优秀的信创数据库&#xff0c;在关基单位中拥有越来越大的用户使用量。 通过SQL注入来写文件一直以来都是SQL注入漏洞深入利用的一种方式&#xff0c;对于不同的数据库通常写文件的方式也是…

探索无限可能性——微软 Visio 2021 改变您的思维方式

在当今信息化时代&#xff0c;信息流动和数据处理已经成为各行各业的关键。微软 Visio 2021 作为领先的流程图和图表软件&#xff0c;帮助用户以直观、动态的方式呈现信息和数据&#xff0c;从而提高工作效率&#xff0c;优化业务流程。本文将介绍 Visio 2021 的特色功能及其在…

【管理咨询宝藏119】翰威特组织架构设计优化方案

本报告首发于公号“管理咨询宝藏”&#xff0c;如需阅读完整版报告内容&#xff0c;请查阅公号“管理咨询宝藏”。 【管理咨询宝藏119】翰威特组织架构设计优化方案 【格式】PDF版本 【关键词】人力资源、组织设计、组织架构 【核心观点】 - 城镇化建设和居民可支配收入的增长…

Python实现定时任务的方式

大家好&#xff0c;在当今数字化的时代&#xff0c;定时任务的需求在各种应用场景中频繁出现。无论是数据的定时更新、周期性的任务执行&#xff0c;还是特定时间点的操作触发&#xff0c;Python 都为我们提供了强大而灵活的手段来实现这些定时任务。当我们深入探索 Python 的世…

代理 模式

一、什么是代理模式 代理模式指代理控制对其他对象的访问&#xff0c;也就是代理对象控制对原对象的引⽤。在某些情况下&#xff0c;⼀个对象不适合或者不能直接被引⽤访问&#xff0c;⽽代理对象可以在客⼾端和⽬标对象之间起到中介的作⽤。 二、为什么使用代理模式 模式作…

MySQL各种锁

目录 1. 从粒度上区分锁 1.1 全局锁&#xff08;第一粒度&#xff09; 1.2 表级锁&#xff08;第二粒度&#xff09; 1.3 行锁&#xff08;第三最小粒度&#xff09; 2 从模式上区分锁 2.1 什么是乐观锁 2.2 什么是悲观锁 2.3 意向共享锁和意向排他锁 2.4 临键锁和记录…

【Python】 深入理解Python中的UnicodeDecodeError及其解决方案

基本原理 在Python编程中&#xff0c;我们经常需要处理各种类型的数据&#xff0c;尤其是文本数据。文本数据在计算机中通常以字节的形式存在&#xff0c;而字节需要被解码成我们能够理解的字符。这个过程涉及到编码和解码的概念。 编码是将字符转换为字节的过程&#xff0c;…

23 vue3面试重难点复习:响应式原理、特点、8大生命钩子、data数据定义、组件、全家桶

vue作为用的最为广泛的当前热门框架&#xff0c;总结如下重难点核心知识&#xff1a; 1.vue特点是什么&#xff1f; 1.1优点 渐进式 vue本身只提供数据响应式&#xff0c;需要全局缓存用 vuex&#xff0c;需要路由用 vue-router 组件化 封装组件&#xff0c;利于复用 响应式数…

k8s——Pod进阶(资源限制和探针)

一、资源限制 1.1 资源限制的定义 当定义Pod时可以选择性地为每个容器设定所需要的资源数量。 最常见的可设定资源是CPU和内存大小&#xff0c;以及其他类型的资源。 当为Pod中的容器指定了request资源时&#xff0c;调度器就使用该信息来决定将Pod调度到哪个节点上。当还为容器…

汇凯金业:量化交易有风险吗

量化交易是一种通过复杂的数学模型和算法在金融市场中进行高频和自动化交易的方式。尽管量化交易在提高市场效率、减少人为错误等方面具有诸多优点&#xff0c;但它也同样存在着不少风险。以下列举了一些主要的风险因素&#xff1a; 1. 模型风险 模型缺陷&#xff1a;量化交易…

网络协议。

一、流程案例 接下来揭秘我要说的大事情&#xff0c;“双十一”。这和我们要讲的网络协议有什么关系呢&#xff1f; 在经济学领域&#xff0c;有个伦纳德里德&#xff08;Leonard E. Read&#xff09;创作的《铅笔的故事》。这个故事通过一个铅笔的诞生过程&#xff0c;来讲述…

数据安全之翼:天空卫士在汽车数据安全领域的卓越领航

近期&#xff0c;中国汽车网络安全与数据安全产业的积极倡导者谈思实验室发布首份《汽车网络与数据安全行业全景图》&#xff0c;天空卫士入选&#xff0c;并且位列榜首。 天空卫士在汽车数据安全领域有丰富的实践经验&#xff0c;曾为多家汽车行业用户提供数据安全产品与服务&…

LeetCode - 贪心(Greedy)算法集合(Python)[分配问题|区间问题]

欢迎关注我的CSDN&#xff1a;https://spike.blog.csdn.net/ 本文地址&#xff1a;https://spike.blog.csdn.net/article/details/139242199 贪心算法&#xff0c;是在每一步选择中&#xff0c;都采取当前状态下&#xff0c;最好或最优&#xff08;即最有利&#xff09;的选择&…

不同linux账户切换不同的cuda版本

原因 由于服务器中安装了两个版本的cuda&#xff08;cuda10.1和cuda11.1&#xff09;&#xff0c;不同项目可能需要应用不同的cuda版本&#xff0c;但是自己又没有root权限或者只想在使用指定conda环境时改为用指定的cuda版本。总结起来有三种方法&#xff1a; 1、修改软链接指…

正点原子[第二期]Linux之ARM(MX6U)裸机篇学习笔记-24.1,2 SPI驱动实验-SPI协议介绍

前言&#xff1a; 本文是根据哔哩哔哩网站上“正点原子[第二期]Linux之ARM&#xff08;MX6U&#xff09;裸机篇”视频的学习笔记&#xff0c;在这里会记录下正点原子 I.MX6ULL 开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了正点原子教学视频和链接中的内容。…

Linux实验六:进程间通信(二)

目录 一、实验目的二、实验内容三、实验环境四、参考代码五、实验步骤步骤1. 编辑源代码test6.c步骤2. 编译源代码test6.c步骤3. 运行可执行程序test6步骤4. 进一步调试源代码test6.c 六、实验结果七、实验总结 一、实验目的 1、理解 POSIX 和 System V 提供的 IPC 相关概念&a…

安防监控视频平台LntonCVS视频监控汇聚平台遏制校园暴力保护校园学生安全应用方案

未成年人被誉为祖国的花朵&#xff0c;是我们国家的未来。然而&#xff0c;最近频繁曝出的未成年霸凌事件却引发了社会的广泛关注。这些事件手段残忍&#xff0c;事态恶劣&#xff0c;引发了全社会对如何保护未成年身心健康、规避霸凌事件发生的深刻思考。 为了更好地保障学生的…

从零开始:如何用Electron将chatgpt-plus.top 打包成EXE文件

文章目录 从零开始&#xff1a;如何用Electron将chatgpt-plus.top 打包成EXE文件准备工作&#xff1a;Node.js和npm国内镜像加速下载初始化你的Electron项目创建你的Electron应用运行你的Electron应用为你的应用设置图标打包成EXE文件结语 从零开始&#xff1a;如何用Electron将…

echarts学习:将echats实例代理为响应式对象可能带来的风险

1.起源 最近我在学习如何封装echarts组件&#xff0c;我所参考的其中一篇博客中提到了一个“图表无法显示的问题”。 根据其中的介绍&#xff0c;造成此种问题的原因是因为&#xff0c;使用ref接受了echarts实例&#xff0c;使得echarts实例被代理为了响应式对象&#xff0c;进…

[C#]使用C#部署yolov8的obb旋转框检测tensorrt模型

【测试通过环境】 win10 x64 vs2019 cuda11.7cudnn8.8.0 TensorRT-8.6.1.6 opencvsharp4.9.0 .NET Framework4.7.2 NVIDIA GeForce RTX 2070 Super 版本和上述环境版本不一样的需要重新编译TensorRtExtern.dll&#xff0c;TensorRtExtern源码地址&#xff1a;TensorRT-CShar…