《恋上数据结构与算法》第1季:链表原理实现(图文并茂)

news2025/7/12 8:01:29

数据结构与算法的学习笔记目录:《恋上数据结构与算法》的学习笔记 目录索引

链表原理实现

  • 一、链表
  • 二、链表的设计
  • 三、链表的接口设计
  • 四、链表接口的实现
    • 1. 索引越界的判断
    • 2. 根据索引查找指定节点
    • 3. 添加数据
    • 4. 插入元素
    • 5. 删除元素
    • 6. 清空元素
    • 7. 修改元素
    • 8. 查找元素
    • 9. 查找元素索引
    • 10. 获取链表存储元素的个数
    • 11. 链表是否为空
    • 12. 判断元素是否存在
    • 13. 打印链表中存储的数据
  • 五、链表的复杂度
  • 六、巩固练习 --- LeetCode算法题
    • 1. LeetCode - [237.删除表中的节点](https://leetcode-cn.com/problems/delete-node-in-a-linked-list/)
    • 2. LeetCode - [206.反转链表](https://leetcode-cn.com/problems/reverse-linked-list/)
    • 3. LeetCode - [141.环形链表](https://leetcode-cn.com/problems/linked-list-cycle/)

动态数组有个明显的缺点,可能会造成内存空间的大量浪费;
链表可以做到用多少就申请多少内存

一、链表

链表是一种 链式存储 的线性表,所有元素的内存地址不一定是连续的。
在这里插入图片描述

二、链表的设计

  1. 创建类 LinkedList ,用来管理链表数据,其中的 size 属性记录存储数据的数量,first 属性引用链表的第0个元素
  2. 创建私有类Node ,其中的 element 属性用于存储元素, next 属性用于指向链表中的下一个节点。
    在这里插入图片描述
public class LinkedList<E> {
    private int size;
    private Node<E> first;
    
    // 私有类,链表中的节点
    private class Node<E>{
        E element;
        Node<E> next;
        
        // 构造方法
        public Node(E element, Node<E> next){
            this.element = element;
            this.next = next;
        }
    }
}

三、链表的接口设计

  • 与动态数组一样,链表也是需要提供增删改查
// 元素的数量
int size(); 
// 是否为空
boolean isEmpty();
// 是否包含某个元素
boolean contains(E element); 
// 添加元素到最后面
void add(E element); 
// 返回index位置对应的元素
E get(int index); 
// 设置index位置的元素
E set(int index, E element); 
// 往index位置添加元素
void add(int index, E element); 
// 删除index位置对应的元素 
E remove(int index); 
// 查看元素的位置
int indexOf(E element); 
// 清除所有元素
void clear(); 

四、链表接口的实现

1. 索引越界的判断

  1. 在操作链表数据时,需要防止索引越界,防止出现程序异常的问题
  2. 查找和删除数据时,需要检查索引的范围是 0 到 size-1 之间,所以方法如下:
// 索引越界判断
private void rangCheck(int index){
    if(index < 0 || index >= size){
        throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
    }
}
  1. 插入数据时,因为可以将数据加到所有数据的最后,所以需要检查索引的范围是 0 到 size 之间
private void rangCheckForAdd(int index){
    if(index < 0 || index > size){
        throw new IndexOutOfBoundsException("Index: " + index + ", Size: " + size);
    }
}

2. 根据索引查找指定节点

  1. 因为链表中的数据都存在节点中,所以操作数据时 需要找到对应的节点
  2. 我们可以实现一个根据索引,来查找指定节点的方法
private Node<E> node(int index){
    rangCheck(index);
    // 头节点,就是first指向的那个节点
    Node<E> node = first;
    // 根据索引遍历,查找对应的节点
    for(int i = 0; i < index; i++){
        node = node.next;
    }
    return node;
}

3. 添加数据

  1. 添加数据时,需要创建一个节点存储数据,并将该节点拼接到最后节点的后面,然后 size 加 1
  2. 两种情况:
  • 第一种:当前链表没有数据,新节点拼接到first
  • 第二种:当前链表有数据,新节点拼接到最后的节点
public void add(E element){
    // 当first等于null时,说明此时没有节点,所以first引用新节点
    if(first == null){
        first = new Node<>(element, null);
    }else{
        // 当first不等于null时,说明链表中有节点,此时获取最后一个节点,并将该节点的next指向新节点
        Node<E> node = node(size-1);
        node.next = new Node<E>(element, null);
    }
}

4. 插入元素

  1. 插入链表时,只需要创建新节点保存元素,然后插入指定位置即可
  2. 两种情况:
  • 第一种:插入到0的位置,需要使用first指向新节点
    在这里插入图片描述

  • 第二种:插入到非0的位置,直接找到前一个节点进行处理
    在这里插入图片描述

public void add(int index, E element){
    // 检查索引是否越界
    rangCheckForAdd(index);
    // 当插入到0的位置
    if(index == 0){
        // 将first指向新节点,新节点的next指向first之前指向的节点
        first = new Node<E>(element, first.next);
    }else {
        // 找到指定位置前面的节点
        Node<E> prev = node(index);
        // 将前面节点的next指向新节点,新节点的next指向prev之前指向的节点
        prev.next = new Node<>(element, prev.next);
    }
    size++;
}
  1. 添加元素的方法可以简写
public void add(E element){
    /*// 当first等于null时,说明此时没有节点,所以first引用新节点
    if(first == null){
        first = new Node<>(element, null);
    }else{
        // 当first不等于null时,说明链表中有节点,此时获取最后一个节点,并将该节点的next指向新节点
        Node<E> node = node(size-1);
        node.next = new Node<E>(element, null);
    }*/
    // 元素添加到size位置,即添加到最后面
    add(size, element);
}

5. 删除元素

  1. 删除元素,找到指定节点前面的节点,然后直接指向需要删除节点的下一个节点即可,然后 size 减 1
  2. 两种情况:
  • 第一种:删除第0个元素,需要使用first指向第1个节点
  • 第二种:删除非0索引的元素,找到前一个节点,然后直接指向下一个节点即可
    在这里插入图片描述
public E remove(int index){
    // 检查索引是否越界
    rangCheck(index);
    // 记录需要删除的节点
    Node<E> old = first;
    // 当删除第0个元素时,将first的next指向索引为“1”的节点即可
    if(index == 0){
        first = first.next;
    }else{
        // 找到前一个元素
        Node<E> prev = node(index - 1);
        // 记录需要删除的节点
        old = prev.next;
        // 将prev的next指向需要删除节点的后一个节点
        prev.next = old.next;
    }
    size--;
    // 返回删除的元素
    return old.element;
}

6. 清空元素

  • 清空元素,直接将 first 指向 null 即可,同时 size 置为 0
    在这里插入图片描述
public void clear(){
    first = null;
    size = 0;
}

7. 修改元素

  • 修改元素,找到对应节点,直接修改元素即可
public E set(int index, E element){
    // 找到对应节点,node方法中已经判断了索引是否越界
    Node<E> node = node(index);
    // 记录旧元素
    E old = node.element;
    // 覆盖元素
    node.element = element;
    // 返回旧元素
    return old;
}

8. 查找元素

  • 查找元素。直接找到对应的节点,取出元素即可
public E get(int index){
    // node方法中已经判断了索引是否越界
    return node(index).element;
}

9. 查找元素索引

  1. 查找指定元素的索引,需要遍历所有节点,找到节点对应的元素与执行元素相等即可
  2. 因为元素可以是 znull ,所以需要分两种情况处理
private static final  int ElEMENT_NOT_FOUND = -1;
public int indexOf(E element){
    // 取出头结点
    Node<E> node = first;
    // 当element为null的处理
    if(element == null){
        // 遍历节点,找到存储为null的节点,返回索引
        for(int i = 0; i < size; i++){
            if(node.element == null){
                return i;
            }
            node = node.next;
        }
    }else{
        for(int i = 0; i < size; i++){
            // 遍历节点,找到存储的元素与指定元素相等的节点,返回索引
            if(element.equals(node.element)){
                return i;
            }
            node = node.next;
        }
    }
    // 没有找到元素对应的节点,返回-1;
    return ElEMENT_NOT_FOUND;
}

10. 获取链表存储元素的个数

  • 获取链表存储元素的个数,就是 size 的值
public int size(){
    return size;
}

11. 链表是否为空

  • 链表是否为空,只需要判断 size 是否等于 0 即可
public boolean isEmpty(){
    return size == 0;
}

12. 判断元素是否存在

  • 链表元素是否存在,只需要判断元素的索引是否为 ElEMENT_NOT_FOUND
public boolean contains(E element){
    return indexOf(element) != ElEMENT_NOT_FOUND;
}

13. 打印链表中存储的数据

  • 只需要重写 toString 方法即可
@Override
public String toString() {
    StringBuilder string = new StringBuilder();
    string.append("size = ").append(size).append(", [");
    Node<E> node = first;
    for (int i = 0; i < size; i++) {
        if (i != 0) {
            string.append(",");
        }
        string.append(node.element);
        node = node.next;
    }
    string.append("]");
    return string.toString();
}

到此为止,我们成功的实现了链表。

五、链表的复杂度

在这里插入图片描述

六、巩固练习 — LeetCode算法题

1. LeetCode - 237.删除表中的节点

在这里插入图片描述

2. LeetCode - 206.反转链表

在这里插入图片描述

3. LeetCode - 141.环形链表

在这里插入图片描述

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

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

相关文章

傻白入门芯片设计,RDL/Interposer/EMIB/TSV(三)

目录 一、再分配层&#xff08;RDL&#xff09; 二、硅中介层&#xff08;Si Interposer&#xff09;&#xff1a;Active and Passive 三、嵌入式硅桥&#xff08;EMIB&#xff09; 四、硅通孔 TSV&#xff08;Through Silicon Vias&#xff09; 一、再分配层&#xff08;R…

CCF CSP认证2022年6月 归一化处理、寻宝!大冒险!、光线追踪

这是我第一次参加了这次CSP考试&#xff0c;300分&#xff0c;写了124三题&#xff0c;模拟题到现在都没看过题面没看&#xff0c;笑&#xff0c;t4写成模拟加数据结构&#xff0c;200行&#xff0c;因为一个小错误调了1h&#xff0c;错失了大好机会。考试环境的VSC配置的字体太…

[一篇读懂]C语言十讲:单链表的新建、查找

[一篇读懂]C语言十讲&#xff1a;单链表的新建、查找1. 与408关联解析及本节内容介绍1 与408关联解析2 本节内容介绍2. 头插法新建链表实战3. 尾插法新建链表实战4. 按位置查找及按值查找实战5. 往第i个位置插入元素实战6. 链表的调试方法总结234561. 与408关联解析及本节内容介…

面对无法投入模型训练的object类型数据在头疼,快来使用我的丝滑小连招

面对无法投入模型训练的object类型数据在头疼&#xff0c;快来使用我的丝滑小连招 前言 丝滑小连招 tip1- get_dummies完美one-hot&#xff08;str->int&#xff09; tip2 - rename_dims解决重名问题&#xff01; tip3 - insert且drop&#xff01;​​​​​​​ 前言 我…

小爱同学控制美的美居中的家电热水器,空调等

背景 家里大多数家电都是支持接入米家App的&#xff0c;美的家电不能接入小米&#xff0c;电脑安装Home Assistant成功实现小爱语音控制美的燃气热水器。 实现步骤&#xff1a; 1. 安装docker 我的电脑是windows的&#xff0c;那就直接安装docker desktop https://desktop.…

【Linux】基础指令(三) —— 收尾篇

文章目录前言zip 和 unzip 指令tar 指令bc 指令uname 指令history关机热键补充ctrl c↑ && ↓ctrl rctrl d指令拓展结语前言 今天为大家带来的是最后一部分基础指令讲解。主要内容为 7个指令讲解、热键补充、简单提一下指令的拓展 。内容相对之前较少&#xff0c;更…

服务器密码以及用户名怎么修改

服务器密码以及用户名怎么修改 我是艾西&#xff0c;今天给大家说下服务器密码如何修改 windows2003系统&#xff1a; 1、右键我的电脑&#xff0c;点击“管理”&#xff1a; 2、在“本地用户和组”中打开“用户”&#xff0c;在右侧找到 Administrator 账户进行修改。 200…

【linux】linux实操篇之任务调度

目录前言crond 任务调度概述基本语法快速入门案例案例一&#xff1a;每隔一分钟将ls -l /etc/ 追加到 /tmp/to.txt 文件案例二&#xff1a;每隔一分钟执行python文件结语前言 我们常用linux做一些定时任务&#xff0c;最常见的就是在服务器领域&#xff0c;我们常常做一些定时…

高分辨率格式理论

一个核心概念&#xff1a;人工粘性 考虑经典的双曲守恒律方程 ∂u∂t∂f∂x0{{\partial u} \over {\partial t}} {{\partial f} \over {\partial x}} 0∂t∂u​∂x∂f​0 可以写成守恒形式的数值格式 uin1uin−λ(f^i1/2n−f^i1/2n)u_i^{n 1} u_i^n - \lambda \left( {\ha…

基于ssm+mysql+jsp学生成绩管理系统(含实训报告)

基于ssmmysqljsp学生成绩管理系统(实训报告&#xff09;一、系统介绍二、功能展示1.学生信息查询2.学生信息添加3.学生信息修改4.学生信息删除四、获取源码一、系统介绍 系统主要功能&#xff1a;系统实现了学生信息查询、添加、修改、删除。 环境配置&#xff1a; Jdk1.8 M…

[附源码]java毕业设计智慧教学平台

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

储能辅助电力系统调峰的容量需求优化配置matlab/cplex

参考文献&#xff1a;储能辅助电力系统调峰的容量需求研究 摘要&#xff1a;建立了储能辅助电力系统调峰的容量需求优化配置模型&#xff0c;设置了含储能和不含储能两种仿真方案&#xff0c;将两个算例代入所提模型进行求解&#xff0c;得到最优的储能系统容量和功率配置&…

Flutter高仿微信-第52篇-群聊-清空聊天记录

Flutter高仿微信系列共59篇&#xff0c;从Flutter客户端、Kotlin客户端、Web服务器、数据库表结构、Xmpp即时通讯服务器、视频通话服务器、腾讯云服务器全面讲解。 详情请查看 效果图&#xff1a; 实现代码&#xff1a; //清空聊天记录对话框 void _cleanGroupChatDialog(){Lo…

【Python】数据类型 + 运算符 + 输入输出

文章目录一. 常量和表达式二. 变量和类型1. 什么是变量2. 变量的语法2.1 定义变量2.2 使用变量3. 变量的类型3.1 整数3.2 浮点数3.3 字符串3.4 布尔3.5 关于变量类型的几点补充三. 注释1. 什么是注释&#xff1f;2. 为什么要有注释&#xff1f;3. 如何写注释&#xff1f;3.1 注…

深度学习制作自己的数据集—为数据集打上标签保存为txt文件,并进行划分和加载数据集

目录 0 前言 1 为图片数据集打上标签并保存为txt文件 2 将txt文件中的图片标签数据集随机划分为训练集和测试集 3 加载txt文件中的图片标签数据集 0 前言 目前是被封控的第四天了&#xff0c;只能呆在宿舍不能出去&#xff0c;记得上次这样子还是一年前大四快毕业那时候了……

CyberController手机外挂番外篇:源代码的二次修改

文章目录前言调试过程中的疑问为什么一段时间不使用CyberController&#xff0c;翻译就无法触发了&#xff1f;为什么连接成功了&#xff0c;但却依然无法进行语音识别和翻译&#xff1f;多长时间TCP连接就会挂掉连接正常与断开连接有什么区别&#xff1f;不停进行翻译&#xf…

现代密码学导论-18-伪随机置换

目录 伪随机置换 PROPOSITION 3.26 伪随机置换和伪随机函数的关系 DEFINITION 3.27 强伪随机置换 伪随机置换 我们称F是含参数k的置换&#xff0c;当且仅当 且对于所有k&#xff0c; Fk是一对一的&#xff0c;即是满射的。 其中 lin 称为F的块长度 对于给定的 k、x和k、y&…

76.【图】

图( 一).图的基本结构(1).无序偶对.(2).有序偶对(3).有向图和无向图(4).权(5).网图(二).图的基本术语(1).邻接.依附(2).有向完全图,无向完全图(3).顶点的度,入度,出度(4).路径 路径长度 回路(5).简单路径 简单回路(6).子图(7).连通图 连通分量(8).强连通图 强连通分量(三).图的…

改进粒子滤波的无人机三维航迹预测方法(基于Matlab代码实现)

&#x1f468;‍&#x1f393;个人主页&#xff1a;研学社的博客 &#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜…

骨传导耳机贴不紧咋办,推荐五款佩戴最稳固的骨传导蓝牙耳机

很多小伙伴都在反馈&#xff0c;骨传导耳机在佩戴时戴不稳&#xff0c;这关乎于耳机的材质有很大的关系&#xff0c;下面就给大家推荐几款佩戴十分牢固的骨传导耳机&#xff0c;不调人群使用哦~看看有没有自己喜欢的吧~ 1、南卡Runner Pro4骨传导蓝牙耳机 &#xffe5;1498 南…