算法通关村第一关-链表青铜挑战笔记
欢迎来到 : 第一关青铜关
- java如何创建链表
- 链表怎么增删改查
我们先了解链表
单链表的概念
我们从简单的创建和增删改查开始.
链表的概念
线性表分为顺序表(数组组成)和链表(节点组成) .
链表又分:
- 单向 双向
- 有哨兵节点 无哨兵节点
- 循环 不循环
链表是一种物理存储单元上非连续、非顺序的存储结构,单链表就像铁链一样,元素之间互相连接。链表由一系列的结点(链表中的每一个元素称为结点也叫节点)组成, 结点可以在运行时动态生成。
在链表中每个节点都有数据域和指针域两部分:
数据域用来存值 , 指针域用来存放地址(下一节点的地址) .
举个简单的例子{1,2,3}用链表存储:
思考一下
思考一下面两个图 , 是否满足单链表的要求 , 为什么 ?
图一:
图二:
解析:
第一图是满足单链表的要求 , 因为我们说链表要求环环相扣,核心是一个结点只能有一个后继,但不代表个结点只能有一个被指向。第一个图中,c1被a2和b3同时指向,这是没关系的。这就好比法律倡导一夫一妻,你只能爱一个人,但是可以都多个人爱你。
第二图就不满足要求了,因为c1有两个后继a5和b4.另外在做题的时候要注意比较的是值还是结点,有时可能两个结点的值相等,但并不是同一个结点,例如下图中有两个结点的值都是1,但并不是同一个结点。
链表的相关概念
节点和头节点
每个点都由值和指向下一个结点的地址组成的独立的单元,称为一个结点,有时也称为节点,含义都在链表中,是一样的。
对于单链表,如果知道了第一个元素,就可以通过遍历访问整个链表,因此第一个结点最重要一般称为头结点
虚拟节点(哨兵节点)
在做题以及在工程里经常会看到虚拟结点的概念,其实就是一个结点dummyNode,其next指针指向head,也就是dummyNode.next=head.
因此,如果我们在算法里使用了虚拟结点,则要注意如果要获得head结点,或者从方法(函数)里返回的时候,则应使用dummyNode.next.
另外注意,dummyNode的val不会被使用,初始化为0或者-1等都是可以的。既然值不会使用,那虚拟结点有啥用呢?简单来说,就是为了方便我们处理首部结点,否则我们需要在代码里单独处理首部结点的问题。在链表反转里,我们会看到该方式可以大大降低解题难度
创建链表
那我们如何使用链表呢?按照面向对象的思想,我们可以设计一个类,来描述结点这个事物,用一个属性描述这个结点存储的元素,用来另外一个属性描述这个结点的下一个结点。
类名 | Node |
构造方法 | Node(T t,Node next):创建Node对象 |
成员变量 | T value:存储数据 Node next:指向下一个结点 |
举例 : 存储值为int类型
/*** 节点类*/
public class Node {//值int value;//地址Node next;public Node(int value, Node next) {this.value = value;this.next = next;}
}
生成链表:
public static void main(String[] args) throws Exception {//构建结点Node<Integer> first = new Node<Integer>(11, null);Node<Integer> second = new Node<Integer>(13, null);Node<Integer> third = new Node<Integer>(12, null);Node<Integer> fourth = new Node<Integer>(8, null);Node<Integer> fifth = new Node<Integer>(9, null);//生成链表first.next = second;second.next = third;third.next = fourth;fourth.next = fifth;}
添加数据 :
public static void main(String[] args) throws Exception {//构建结点Node<Integer> first = new Node<Integer>(11, null);Node<Integer> second = new Node<Integer>(13, null);Node<Integer> third = new Node<Integer>(12, null);Node<Integer> fourth = new Node<Integer>(8, null);Node<Integer> fifth = new Node<Integer>(9, null);//生成链表first.next = second;second.next = third;third.next = fourth;fourth.next = fifth;//添加数据Node<Integer> six= new Node<Integer>(22, null);six.next = second;first.next = six;}
删除数据:
public static void main(String[] args) throws Exception {//构建结点Node<Integer> first = new Node<Integer>(11, null);Node<Integer> second = new Node<Integer>(13, null);Node<Integer> third = new Node<Integer>(12, null);Node<Integer> fourth = new Node<Integer>(8, null);Node<Integer> fifth = new Node<Integer>(9, null);//生成链表first.next = second;second.next = third;third.next = fourth;fourth.next = fifth;//添加数据Node<Integer> six= new Node<Integer>(22, null);six.next = second;first.next = six;//删除数据first.next = second;}
修改数据:
修改值就很简单了找到节点直接修改就可以了:
first.value = 100;
单向链表
单向链表是链表的一种,它由多个结点组成,每个结点都由一个数据域和一个指针域组成,数据域用来存储数据, 指针域用来指向其后继结点。链表的头结点的数据域不存储数据,指针域指向第一个真正存储数据的结点。
单向链表设计 :
类名 | LinkList |
构造方法 | LinkList():创建LinkList对象 |
成员方法 | 1.public void clear():空置线性表 2.publicboolean isEmpty():判断线性表是否为空,是返回true,否返回false 3.public int length():获取线性表中元素的个数 4.public T get(int i):读取并返回线性表中的第i个元素的值 5.public void insert(T t):往线性表中添加一个元素; 6.public void insert(int i,T t):在线性表的第i个元素之前插入一个值为t的数据元素。 7.public T remove(int i):删除并返回线性表中第i个数据元素。 8.public int indexOf(T t):返回线性表中首次出现的指定的数据元素的位序号,若不存在,则 返回-11。 |
成员内部 类 | private class Node:结点类 |
成员变量 | 1.private Node head:记录首结点 2.private int N:记录链表的长度 |
/*** 单链表 (虚拟节点)* @param <T>*/
public class LinkList<T> {//记录头结点private Node head;//记录链表的长度private int N;public LinkList() {//初始化头结点head = new Node(null, null);N = 0;}//清空链表public void clear() {head.next = null;head.item = null;N = 0;}//获取链表的长度public int length() {return N;}//判断链表是否为空public boolean isEmpty() {return N == 0;}//获取指定位置i出的元素public T get(int i) {if (i < 0 || i >= N) {throw new RuntimeException("位置不合法!");}Node n = head.next;for (int index = 0; index < i; index++) {n = n.next;}return n.item;}//向链表中添加元素tpublic void insert(T t) {//找到最后一个节点Node n = head;while (n.next != null) {n = n.next;}Node newNode = new Node(t, null);n.next = newNode;//链表长度+1N++;}//向指定位置i处,添加元素tpublic void insert(int i, T t) {if (i < 0 || i >= N) {throw new RuntimeException("位置不合法!");}//寻找位置i之前的结点Node pre = head;for (int index = 0; index <= i - 1; index++) {pre = pre.next;}//位置i的结点Node curr = pre.next;Node newNode = new Node(t, curr);//让之前的结点指向新结点pre.next = newNode;//长度+1N++;}//删除指定位置i处的元素,并返回被删除的元素public T remove(int i) {if (i < 0 || i >= N) {throw new RuntimeException("位置不合法");}//寻找i之前的元素Node pre = head;for (int index = 0; index <= i - 1; index++) {pre = pre.next;}//当前i位置的结点Node curr = pre.next;//前一个结点指向下一个结点,删除当前结点pre.next = curr.next;//长度-1N--;return curr.item;}//查找元素t在链表中第一次出现的位置public int indexOf(T t) {Node n = head;for (int i = 0; n.next != null; i++) {n = n.next;if (n.item.equals(t)) {return i;}}return -1;}//结点类private class Node {//存储数据T item;//下一个结点Node next;public Node(T item, Node next) {this.item = item;this.next = next;}}
}
测试 :
public class LinkTest {public static void main(String[] args) {LinkList<String> list = new LinkList<>();list.insert("aa");list.insert("bb");list.insert(1,"cc");list.insert("dd");for (int i = 0; i < list.length(); i++) {System.out.println(list.get(i));}list.remove(1);System.out.println(list.length());}
}
只要设计合理 , 都可以!
简化一点的版本 :
/*** 单向链表*/
public class SinglyLinkedList {//哨兵(头指针)private Node head = null;//节点类private static class Node {int data;Node next;public Node(int data, Node next) {this.data = data;this.next = next;}}/*** 向链表头部插入** @param value*/public void addFirst(int value) {//1.链表为空的情况//head = new Node(value, null);//2.链表非空head = new Node(value, head);}/*** 遍历*/public void foreach(Consumer<Integer> consumer) {Node p = head;while (p != null) {consumer.accept(p.data);p = p.next;}}/*** 找到最后一个节点** @return*/private Node findLast() {if (head == null) {return null;}Node p = head;while (p.next != null) {p = p.next;}return p;}/*** 在链表尾部添加节点** @param value*/public void addLast(int value) {Node last = findLast();if (last == null) {addFirst(value);return;}last.next = new Node(value, null);}/*** 根据索引查找** @param index* @return*/private Node findNode(int index) {int i = 0;for (Node p = head; p != null; p = p.next, i++) {if (i == index) {return p;}}return null;}/*** 根据索引获取值** @param index* @return*/public int get(int index) {Node node = findNode(index);if (node == null) {throw new IllegalArgumentException(String.format("index is error"));}return node.data;}/*** 向索引位置插入数据** @param index* @param value*/public void insert(int index, int value) {if (index == 0) {addFirst(value);return;}Node node = findNode(index - 1);if (node == null) {throw new IllegalArgumentException(String.format("index is error"));}node.next = new Node(value, node.next);}/*** 删除头*/public void removeFirst() {if (head == null) {throw new IllegalArgumentException("Null");} else {head = head.next;}}/*** 按索引删除** @param index*/public void removeIndex(int index) {if (index == 0) {removeFirst();} else {Node node = findNode(index - 1);if (node == null) {throw new IllegalArgumentException("error");}if (node.next == null) {throw new IllegalArgumentException("error");}node.next = node.next.next;}}}
测试大家自己练习一下......
双向链表
双向链表也叫双向表,是链表的一种,它由多个结点组成,每个结点都由一个数据域和两个指针域组成,数据域用 来存储数据,其中一个指针域用来指向其后继结点,另一个指针域用来指向前驱结点。链表的头结点的数据域不存 储数据,指向前驱结点的指针域值为null,指向后继结点的指针域指向第一个真正存储数据的结点。
简单写了一下 , 伙伴们自己完善和修改吧 :
/*** 双链表*/public class TwoLinkList {//哨兵节点private Node head = new Node(null, -1, null);//节点private static class Node {Node pre;int value;Node next;public Node(Node pre, int value, Node next) {this.pre = pre;this.value = value;this.next = next;}}/*** 查找尾节点** @return*/private Node findLastNode() {Node node = head;while (node.next != null ) {node = node.next;}return node;}/*** 尾插入** @param value*/public void insert(int value) {Node lastNode = findLastNode();lastNode.next = new Node(lastNode,value,null);}/*** 头插入** @param value*/public void addFist(int value) {//插入Node node = head;node.next=new Node(head,value,head.next);}/*** 遍历*/public void forEach() {if (head.next == null) {System.out.println("null!");}else {Node p = head.next;while (p != null) {System.out.println(p.value);p = p.next;}}}
}
测试 :
public class TwoLinkListTest {public static void main(String[] args) {TwoLinkList twoLinkList = new TwoLinkList();twoLinkList.addFist(1);twoLinkList.addFist(2);twoLinkList.addFist(3);twoLinkList.addFist(4);twoLinkList.insert(4);twoLinkList.forEach();}
}
这关就到这里了, 朋友们下一关见!
相关文章:

算法通关村第一关-链表青铜挑战笔记
欢迎来到 : 第一关青铜关 java如何创建链表链表怎么增删改查 我们先了解链表 单链表的概念 我们从简单的创建和增删改查开始. 链表的概念 线性表分为顺序表(数组组成)和链表(节点组成) . 链表又分: 单向 双向有哨兵节点 无哨兵节点循环 不循环 链表是一种物理存储单…...

✔ ★【备战实习(面经+项目+算法)】 10.15学习时间表
✔ ★【备战实习(面经项目算法)】 坚持完成每天必做如何找到好工作1. 科学的学习方法(专注!效率!记忆!心流!)2. 每天认真完成必做项,踏实学习技术 认真完成每天必做&…...
pytorch 训练时raise EOFError EOFError
训练到一半时获取验证数据报错 报错代码 imgs next(iter(val_dataloader)) val_dataloader DataLoader(ImageDataset("data/%s" % opt.dataset_name, transforms_transforms_, unalignedTrue, mode"test"),batch_size5,shuffleTrue,num_workers2,)def …...

node.js+NPM包管理器+Webpack打包工具+前端项目搭建
javascript运行环境(无需依赖html文件) BFF,服务于前端的后端 官网下载安装,node -v查看是否安装成功 ①、创建一个01.js文件 //引入http模块 const httprequire(http)//创建服务器 http.createServer(function(request,respo…...
PCL点云处理之基于FPFH特征的全局配准流程具体实现(二百二十一)
PCL点云处理之基于FPFH特征的全局配准流程具体实现(二百二十一) 一、算法介绍二、算法实现1.代码2.效果一、算法介绍 PCL点云库提供的多种工具,可以组合为一套完整的点云配准流程,这里选择FPFH特征,进行具体的配准流程实现,主要内容包括点云读取、点云法线计算、点云特征…...

ai_drive67_基于不确定性的多视图决策融合
论文链接:https://openreview.net/forum?idOOsR8BzCnl5 https://arxiv.org/abs/2102.02051 代码链接:https://github.com/hanmenghan/TMC Zongbo Han, Changqing Zhang, Huazhu Fu, Joey Tianyi Zhou, Trusted Multi-View Classification, Internatio…...

Docker逃逸---procfs文件挂载
一、产生原因 将宿主机/proc目录挂载进了容器,而该目录内的/proc/sys/kernel/core_pattern文件是负责进程奔溃时内存数据转储的,当第一个字符是| 管道符时,后面的部分会以命令行的方式进行解析并运行,攻击者可以将恶意文件写入该…...

[Python小项目] 从桌面壁纸到AI绘画
从桌面壁纸到AI绘画 一、前言 1.1 确认问题 由于生活和工作需要,小编要长时间的使用电脑,小编又懒,一个主题用半年的那种,所以桌面壁纸也是处于常年不更换的状态。即时改变主题也是在微软自带的壁纸中选择,而这些自…...

【Docker 内核详解】namespace 资源隔离(五):User namespaces
【Docker 内核详解 - namespace 资源隔离】系列包含: namespace 资源隔离(一):进行 namespace API 操作的 4 种方式namespace 资源隔离(二):UTS namespace & IPC namespacenamespace 资源隔…...

网络原理必知会
衔接上文:网络原理必知会_念君思宁的博客-CSDN博客 流量控制: 流量控制也是保证可靠性的机制 对于滑动窗口,批量发送数据而言,窗口越大,相当于批量发送的数据越多,整体的速度也就越快了,但是&…...

ELK 日志分析系统介绍与部署
目录 一、ELK 简介: 1.开源工具介绍: 2.其它组件: 2.1 Filebeat: 2.2 Fluentd: 2.3 缓存/消息队列(redis、kafka、RabbitMQ等): 3. filebeat 结合 logstash 带来好处: 二、为什么要…...

Android 内存治理之线程
1、 前言 当我们在应用程序中启动一个线程的时候,也是有可能发生OOM错误的。当我们看到以下log的时候,就说明系统分配线程栈失败了。 java.lang.OutOfMemoryError: pthread_create (1040KB stack) failed: Out of memory这种情况可能是两种原因导致的。…...
三、K8S之ReplicaSet
ReplicaSet 一、概述 Kubernetes最核心的功能是编排,编排操作都是依靠控制器对象来完成,高级控制器控制着基础的控制器,基础控制器再去控制Pod,Pod里面再包容器。K8S项目里API对象层级大概就是这样。 而ReplicaSet这个控制器是…...

【基础篇】四、本地部署Flink
文章目录 1、本地独立部署会话模式的Flink2、本地独立部署会话模式的Flink集群3、向Flink集群提交作业4、Standalone方式部署单作业模式5、Standalone方式部署应用模式的Flink Flink的常见三种部署方式: 独立部署(Standalone部署)基于K8S部署…...
简述什么是迭代器(Iterator)?
迭代器(Iterator)是一种设计模式,Java 中的迭代器是集合框架中的一个接口,它可以让程序员遍历集合中的元素而无需暴露集合的内部结构。使用迭代器可以遍历任何类型的集合,例如 List、Set 和 Map 等。 通过调用集合类的 iterator() 方法可以获取一个迭代器,并使用 hasNext…...

DarkGate恶意软件通过消息服务传播
导语 近日,一种名为DarkGate的恶意软件通过消息服务平台如Skype和Microsoft Teams进行传播。它冒充PDF文件,利用用户的好奇心诱使其打开,进而下载并执行恶意代码。这种攻击手段使用了Visual Basic for Applications(VBA࿰…...
LeetCode——动态规划篇(六)
刷题顺序及思路来源于代码随想录,网站地址:https://programmercarl.com 目录 300. 最长递增子序列 - 力扣(LeetCode) 674. 最长连续递增序列 - 力扣(LeetCode) 718. 最长重复子数组 - 力扣(…...
sql 注入(2), 文件读写 木马植入 远程控制
sql 注入 文件读写 木马植入 远程控制 一, 检测读写权限 查看mysql全局变量 SHOW GLOBAL VARIABLES LIKE %secure%secure_file_priv 空, 则任意读写secure_file_priv 路径, 则只能读写该路径下的文件secure_file_priv NULL, 则禁止读写二, 读取文件, 使用 load_file() 函数…...

求直角三角形第三点的坐标
文章目录 求直角三角形第三点的坐标1. 原理2. 数学公式3. 推导过程 求直角三角形第三点的坐标 1. 原理 已知内容有: P1、P2 两点的坐标; dis1 为 P1与P2两点之间的距离; dis2 为 P2与P3两点之间的距离; 求解: …...

【Kotlin精简】第3章 类与接口
1 简介 Kotlin类的声明和Java没有什么区别,Kotlin中,类的声明也使用class关键字,如果只是声明一个空类,Kotlin和Java没有任何区别,不过定义类的其他成员会有一些区别。实例化类不用写new,类被继承或者重写…...

C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
SciencePlots——绘制论文中的图片
文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了:一行…...

阿里云ACP云计算备考笔记 (5)——弹性伸缩
目录 第一章 概述 第二章 弹性伸缩简介 1、弹性伸缩 2、垂直伸缩 3、优势 4、应用场景 ① 无规律的业务量波动 ② 有规律的业务量波动 ③ 无明显业务量波动 ④ 混合型业务 ⑤ 消息通知 ⑥ 生命周期挂钩 ⑦ 自定义方式 ⑧ 滚的升级 5、使用限制 第三章 主要定义 …...
【SpringBoot】100、SpringBoot中使用自定义注解+AOP实现参数自动解密
在实际项目中,用户注册、登录、修改密码等操作,都涉及到参数传输安全问题。所以我们需要在前端对账户、密码等敏感信息加密传输,在后端接收到数据后能自动解密。 1、引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId...
大语言模型如何处理长文本?常用文本分割技术详解
为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
大数据学习(132)-HIve数据分析
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言Ǵ…...
DeepSeek 技术赋能无人农场协同作业:用 AI 重构农田管理 “神经网”
目录 一、引言二、DeepSeek 技术大揭秘2.1 核心架构解析2.2 关键技术剖析 三、智能农业无人农场协同作业现状3.1 发展现状概述3.2 协同作业模式介绍 四、DeepSeek 的 “农场奇妙游”4.1 数据处理与分析4.2 作物生长监测与预测4.3 病虫害防治4.4 农机协同作业调度 五、实际案例大…...

GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...

云原生安全实战:API网关Kong的鉴权与限流详解
🔥「炎码工坊」技术弹药已装填! 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关(API Gateway) API网关是微服务架构中的核心组件,负责统一管理所有API的流量入口。它像一座…...