五分钟“手撕”链表

news2024/11/16 7:31:10

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

目录

一、链表的实现代码

二、什么是链表

三、链表的分类

四、链表的常见操作

插入

删除 

五、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)

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

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

相关文章

基恩士PLC与ModbusTCP转Profibus网关实现与激光设备的高效连接

本文将探讨如何通过使用基恩士PLC以及无锡耐特森ModbusTCP转Profibus网关来实现与激光设备的高效连接。在当今工业自动化领域&#xff0c;不同厂商的硬件设备和软件系统之间的互联互通性成为了提高生产效率、实现智能制造的关键因素。其中&#xff0c;可编程逻辑控制器&#xf…

spring boot 3.x版本 引入 swagger2启动时报错

一&#xff0c;问题 Spring Boot 3.x版本的项目里&#xff0c;准备引入Swagger2作为接口文档&#xff0c;但是项目启动报错&#xff1a; java.lang.TypeNotPresentException: Type javax.servlet.http.HttpServletRequest not present at java.base/sun.reflect.generics.…

高等教育的AI革新:OpenAI面向大学推出ChatGPT Edu

OpenAI推出了ChatGPT Edu&#xff0c;这是一个为大学设计的专用版本&#xff0c;旨在让学生、教职员工、研究人员和校园运营能够负责任地使用AI。 ChatGPT Edu 将AI技术引入了教育领域&#xff0c;其建立在GPT-4o的基础上&#xff0c;它不仅能够处理文本和图像&#xff0c;还…

【linux深入剖析】进程间通信

&#x1f341;你好&#xff0c;我是 RO-BERRY &#x1f4d7; 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f384;感谢你的陪伴与支持 &#xff0c;故事既有了开头&#xff0c;就要画上一个完美的句号&#xff0c;让我们一起加油 目录 1.进程间通信目的2. 什么…

dubbo复习:(17)dubbo连接skywalking

一、准备skywalking 8.7.0并启动 二、配置好skywalking agent 三、服务提供者端的依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema…

结构体中内存的对齐

前言 学C的同学应该知道~ 想精通C语言就不得不面对—指针与内存 续上次指针进阶&#xff0c;这一章我来聊一聊C语言内存对齐的问题 学习结构体的你有没有注意过结构体向系统申请的内存为多少呢的&#x1f601; 思考 #include<stdio.h> typedef struct s1 {char a;char …

Firebase Local Emulator Suite详解

文章目录 Firebase Local Emulator Suite 组件安装和使用步骤1. 安装 Firebase CLI2. 初始化 Firebase 项目3. 配置模拟器4. 启动模拟器5. 配置应用程序使用本地模拟器 常见用途 Firebase Local Emulator Suite 是一组本地服务&#xff0c;可以模拟 Firebase 平台的在线服务&am…

抖音电商“山货上头条”走进广东茂名,助销白糖罂荔枝地标农产品

受异常天气影响&#xff0c;今年广西、广东等地“桂味”和“香荔”等荔枝品种罕见减产。入夏以来&#xff0c;“痛失荔枝自由”“荔枝价格暴涨”等话题频上热搜。 为帮助消费者尝到品质良好、价格实惠的新鲜荔枝&#xff0c;今年“抖音商城618好物节”活动期间&#xff0c;抖音…

优化CPU占用率及内存占用2

在标准化无线通信板时&#xff0c;关注过程序占用ram的问题&#xff0c;当时 发现每一个线程都会分配8M栈空间&#xff0c;这次换rk3568后&#xff0c;偶尔看了下RAM占用&#xff0c;吓了一跳&#xff0c;不但每个线程有8M栈空间&#xff0c;几乎每个线程都占用了64MB的一个RAM…

实战Qt 5:打造健壮性计算器的过程

新书上架~&#x1f447;全国包邮奥~ python实用小工具开发教程http://pythontoolsteach.com/3 欢迎关注我&#x1f446;&#xff0c;收藏下次不迷路┗|&#xff40;O′|┛ 嗷~~ 目录 一、项目概述与初始化 二、功能测试与调整 三、性能优化与健壮性提升 四、项目总结与展望…

MathType2025数学公式编辑器有哪些新增功能?

在当今科技飞速发展的时代&#xff0c;数学、科学和工程教育领域对于精确性和效率的要求日益增高。随着教育数字化的不断深入&#xff0c;传统的教学方式和科研手段正逐渐被新型的数字工具所取代。在这一过程中&#xff0c;MathType作为一款强大的数学公式编辑器&#xff0c;其…

Qt 插件机制使用及原理

目录 1.引言 2.插件原理 3.插件实现 3.1.定义一个接口集(只有纯虚函数的类) 3.2.实现接口 4.插件的加载 4.1.静态插件 4.1.1.静态插件实现方式 4.1.2.静态插件加载的过程 4.1.3.示例 4.2.动态插件 4.2.1.动态插件的加载过程 5.定位插件 6.插件开发的优势 7.总结…

【MySQL数据库】:MySQL复合查询

目录 基本查询回顾 多表查询 自连接 子查询 单行子查询 多行子查询 多列子查询 在from子句中使用子查询 合并查询 前面我们讲解的mysql表的查询都是对一张表进行查询&#xff0c;在实际开发中这远远不够。 基本查询回顾 【MySQL数据库】&#xff1a;MySQL基本查…

电子电气SCI期刊,中科院1区TOP,收稿范围广泛

一、期刊名称 IEEE Transactions on Smart Grid 二、期刊简介概况 期刊类型&#xff1a;SCI 学科领域&#xff1a;工程技术 影响因子&#xff1a;9.6 中科院分区&#xff1a;1区 三、期刊征稿范围 IEEE Transactions on Smart Grid是一本跨学科期刊&#xff0c;旨在传播智…

【Linux】进程(1)

大家好&#xff0c;我是苏貝&#xff0c;本篇博客带大家了解Linux进程&#xff08;1&#xff09;&#xff0c;如果你觉得我写的还不错的话&#xff0c;可以给我一个赞&#x1f44d;吗&#xff0c;感谢❤️ 目录 1. 冯诺依曼体系结构2.操作系统&#xff08;Operator System / O…

mysql的增删查改(进阶)

一. 更复杂的新增 将从表名查询到的结果插入到表名2中 insert into 表名2 select .. from 表名1 ...; 创建一个学生表: 创建一个学生表2, 将学生表中的数据加到学生表2中: 注意: 列的类型可以匹配即可插入, 列名和列的类型不一定要完全一致 二. 查询 2.1 聚合查询 前面谈到…

深度学习知识与心得

目录 深度学习简介 传统机器学习 深度学习发展 感知机 前馈神经网络 前馈神经网络&#xff08;BP网络&#xff09; 深度学习框架讲解 深度学习框架 TensorFlow 一个简单的线性函数拟合过程 卷积神经网络CNN&#xff08;计算机视觉&#xff09; 自然语言处理NLP Wo…

LabVIEW中进行步进电机的位置控制

在LabVIEW中进行步进电机的位置控制&#xff0c;通常涉及以下几个关键步骤&#xff1a;设置硬件、配置通信、编写控制算法和实施反馈控制。以下是一个详细的介绍。 硬件设置 步进电机&#xff1a;选择合适的步进电机&#xff0c;根据负载和应用需求选择适当的步数和转矩。 驱…

【力扣】1312. 让字符串成为回文串的最少插入次数

一、题目描述 二、题解 本题我们利用动态规划的思想来解决。 1、状态表示 首先创建一个dp数组&#xff0c;dp[i][j] 表示的是将字符串 s 的 [ i, j ] 区间的这一子串&#xff0c;变成回文串的最少插入次数。 2、状态转移方程 3、初始化 根据「状态转移方程」&#xff0c;没…

煤矿输送设备无人化运维巡检解决方案

一、煤矿行业目前存在的挑战和难题 煤矿行业面临着复杂的环境&#xff0c;如粉尘、潮湿、高温、高瓦斯等&#xff0c;对巡检设备和人员安全有威胁。并且设备分布广、需要长时间作业&#xff0c;全面巡检难度大、对巡检工作的耐力和持续性要求高。而煤矿输送设备无人化运维巡检…