【UCB CS 61B SP24】Lecture 4 - Lists 2: SLLists学习笔记
本文内容为重写上一节课中的单链表,将其重构成更易于用户使用的链表,实现多种操作链表的方法。
1. 重构单链表SLList
在上一节课中编写的 IntList 类是裸露递归的形式,在 Java 中一般不会这么定义,因为这样用户可能需要非常了解引用,并且能够递归地思考,才能使用这个类。
因此我们需要实现一个更容易使用的单链表(Singly Linked List),首先我们定义节点类 IntNode:
package CS61B.Lecture4;public class IntNode {public int val;public IntNode next;public IntNode(int val, IntNode next) {this.val = val;this.next = next;}
}
现在就可以创建单链表类 SLList:
package CS61B.Lecture4;public class SLList {private IntNode head;public SLList(int val) {this.head = new IntNode(val, null);}public static void main(String[] args) {SLList L = new SLList(5);}
}
之前用户使用单链表需要这样定义:IntList L = new IntList(5, null);,他需要有递归考虑的思想,必须指定所指向的下一个链表的空值。
由于 IntNode 类只有在 SLList 类中使用才有意义,且用户一般不会单独去使用 IntNode 类,因此可以将其作为嵌套类放入 SLList 中,并且可以使用 private 关键字控制其权限:
package CS61B.Lecture4;public class SLList {private static class IntNode {public int val;public IntNode next;public IntNode(int val, IntNode next) {this.val = val;this.next = next;}}private IntNode head;public SLList(int val) {this.head = new IntNode(val, null);}public static void main(String[] args) {SLList L = new SLList(5);}
}
注意到我们还将 IntNode 类定义为静态的,静态嵌套类与外部类的实例无关,它更像是一个独立的类,但逻辑上仍然属于外部类的范畴,静态嵌套类可以直接访问外部类的静态字段和方法,但不能直接访问外部类的实例字段和方法(除非通过外部类的实例),因此静态嵌套类通常用于逻辑上与外部类相关,但不需要依赖外部类实例的场景。
2. 实现操作链表的方法
现在我们实现操作链表的几种方法:
package CS61B.Lecture4;public class SLList {private static class IntNode {public int val;public IntNode next;public IntNode(int val, IntNode next) {this.val = val;this.next = next;}}private IntNode head;public SLList(int val) {this.head = new IntNode(val, null);}// 获取首部节点值public int getFirst() {return this.head.val;}// 在首部添加节点public void addFirst(int val) {this.head = new IntNode(val, this.head);}// 删除首部节点并返回其值public int removeFirst() {if (this.head == null) {System.out.println("Already Empty!");return 0;}int val = this.head.val;this.head = this.head.next;return val;}// 获取尾部节点值public int getLast() {IntNode p = this.head;while (p.next != null) p = p.next;return p.val;}// 在尾部添加节点public void addLast(int val) {IntNode p = this.head;while (p.next != null) p = p.next;p.next = new IntNode(val, null);}// 删除尾部节点并返回其值public int removeLast() {if (this.head == null) {System.out.println("Already Empty!");return 0;}int val;if (this.head.next == null) {val = this.head.val;this.head = null;} else {IntNode p = this.head;while (p.next.next != null) p = p.next;val = p.next.val;p.next = null;}return val;}// 获取链表长度public int size() {return this.size(this.head); // 无法在该方法中直接实现递归,需要一个额外的辅助方法}// size()递归辅助方法private int size(IntNode p) {if (p.next == null) return 1;return 1 + size(p.next);}// 重写输出SLList对象的信息@Overridepublic String toString() {if (this.head == null) return "[]";StringBuilder res = new StringBuilder("SLList: [");IntNode p = this.head;while (p != null) {res.append(p.val);if (p.next != null) res.append(", ");else res.append("]");p = p.next;}return res.toString();}public static void main(String[] args) {SLList L = new SLList(5);L.addFirst(10);System.out.println(L.getFirst()); // 10L.addLast(15);System.out.println(L.getLast()); // 15System.out.println(L.size()); // 3System.out.println(L); // SLList: [10, 5, 15]System.out.println(L.removeFirst()); // 10System.out.println(L.removeLast()); // 15System.out.println(L.size()); // 1System.out.println(L); // SLList: [5]}
}
需要重点思考的是如何用递归的方式求解链表的长度,我们使用的是构造一个辅助方法 size(IntNode p) 使得用户可以通过 size() 方法直接获取链表长度。
3. 优化效率
我们现在关注 size() 方法,发现每次用户需要查看链表长度时都需要遍历一次链表,因此我们可以用一个变量实时记录链表的长度,也就是用空间换时间,这样每次查询只需要 O ( 1 ) O(1) O(1) 的时间复杂度,这也是上一节课中直接裸露递归的类 IntList 所无法实现的:
package CS61B.Lecture4;public class SLList {private static class IntNode {public int val;public IntNode next;public IntNode(int val, IntNode next) {this.val = val;this.next = next;}}private IntNode head;private int size;public SLList(int val) {this.head = new IntNode(val, null);this.size = 1;}// 获取首部节点值public int getFirst() {return this.head.val;}// 在首部添加节点public void addFirst(int val) {this.head = new IntNode(val, this.head);this.size++;}// 删除首部节点并返回其值public int removeFirst() {if (this.head == null) {System.out.println("Already Empty!");return 0;}int val = this.head.val;this.head = this.head.next;this.size--;return val;}// 获取尾部节点值public int getLast() {IntNode p = this.head;while (p.next != null) p = p.next;return p.val;}// 在尾部添加节点public void addLast(int val) {IntNode p = this.head;while (p.next != null) p = p.next;p.next = new IntNode(val, null);this.size++;}// 删除尾部节点并返回其值public int removeLast() {if (this.head == null) {System.out.println("Already Empty!");return 0;}int val;if (this.head.next == null) {val = this.head.val;this.head = null;} else {IntNode p = this.head;while (p.next.next != null) p = p.next;val = p.next.val;p.next = null;}this.size--;return val;}// 获取链表长度public int size() {return this.size;}// 重写输出SLList对象的信息@Overridepublic String toString() {if (this.head == null) return "[]";StringBuilder res = new StringBuilder("SLList: [");IntNode p = this.head;while (p != null) {res.append(p.val);if (p.next != null) res.append(", ");else res.append("]");p = p.next;}return res.toString();}public static void main(String[] args) {SLList L = new SLList(5);L.addFirst(10);System.out.println(L.getFirst()); // 10L.addLast(15);System.out.println(L.getLast()); // 15System.out.println(L.size()); // 3System.out.println(L); // SLList: [10, 5, 15]System.out.println(L.removeFirst()); // 10System.out.println(L.removeLast()); // 15System.out.println(L.size()); // 1System.out.println(L); // SLList: [5]}
}
4. 修复addLast()
首先我们写一个无参的构造方法,这样用户能够创建一个空链表:
public SLList() {this.head = null;this.size = 0;
}
现在我们尝试创建空链表,并用 addLast() 方法添加节点:
SLList L = new SLList();
L.addLast(10);
System.out.println(L);
会发现程序报错了,因为空链表的 this.head 就为 null,在执行 while (p.next != null) 时无法获取 p.next,因此我们可以这样修改 addLast() 方法:
// 在尾部添加节点
public void addLast(int val) {if (this.head == null) this.head = new IntNode(val, null);else {IntNode p = this.head;while (p.next != null) p = p.next;p.next = new IntNode(val, null);}this.size++;
}
5. 虚拟头节点
现在观察代码会发现有些方法里做了形如 if (this.head == null) 的特判,当工程代码量变大时如果习惯这么写会导致代码冗余复杂。
我们可以添加一个虚拟头节点,这样就不会存在头节点为空的情况,这种虚拟头节点也称哨兵节点,并且修改 remove 方法使其不返回节点值,因为可以通过 get 方法获取节点值。最后本节课的完整实现代码如下:
package CS61B.Lecture4;public class SLList {private static class IntNode {public int val;public IntNode next;public IntNode(int val, IntNode next) {this.val = val;this.next = next;}}private IntNode sentinel; // 第一个节点在sentinel.next上private int size;public SLList() {this.sentinel = new IntNode(0, null);this.size = 0;}public SLList(int val) {this.sentinel = new IntNode(0, new IntNode(val, null));this.size = 1;}// 获取首部节点值public int getFirst() {IntNode p = this.sentinel;if (p.next != null) p = p.next;return p.val;}// 在首部添加节点public void addFirst(int val) {this.sentinel.next = new IntNode(val, this.sentinel.next);this.size++;}// 删除首部节点public void removeFirst() {if (this.sentinel.next == null) return;this.sentinel.next = this.sentinel.next.next;this.size--;}// 获取尾部节点值public int getLast() {IntNode p = this.sentinel;while (p.next != null) p = p.next;return p.val;}// 在尾部添加节点public void addLast(int val) {IntNode p = this.sentinel;while (p.next != null) p = p.next;p.next = new IntNode(val, null);this.size++;}// 删除尾部节点public void removeLast() {if (this.sentinel.next == null) return;IntNode p = this.sentinel;while (p.next.next != null) p = p.next;p.next = null;this.size--;}// 获取链表长度public int size() {return this.size;}// 重写输出SLList对象的信息@Overridepublic String toString() {StringBuilder res = new StringBuilder("SLList: [");IntNode p = this.sentinel;while (p.next != null) {res.append(p.next.val);p = p.next;if (p.next != null) res.append(", ");}res.append("]");return res.toString();}public static void main(String[] args) {SLList L = new SLList();System.out.println(L.size()); // 0System.out.println(L); // SLList: []L.addLast(15);System.out.println(L.getLast()); // 15L.addFirst(10);System.out.println(L.getFirst()); // 10System.out.println(L.size()); // 2System.out.println(L); // SLList: [10, 15]L.removeLast();L.removeFirst();L.removeLast();L.removeFirst();System.out.println(L.size()); // 0System.out.println(L); // SLList: []}
}
相关文章:
【UCB CS 61B SP24】Lecture 4 - Lists 2: SLLists学习笔记
本文内容为重写上一节课中的单链表,将其重构成更易于用户使用的链表,实现多种操作链表的方法。 1. 重构单链表SLList 在上一节课中编写的 IntList 类是裸露递归的形式,在 Java 中一般不会这么定义,因为这样用户可能需要非常了解…...
【科研绘图系列】R语言绘制小提琴图、散点图和韦恩图(violin scatter plot Venn)
禁止商业或二改转载,仅供自学使用,侵权必究,如需截取部分内容请后台联系作者! 文章目录 介绍加载R包数据下载画图1画图2画图3画图4画图5画图6画图7参考介绍 【科研绘图系列】R语言绘制小提琴图、散点图和韦恩图(violin & scatter plot & Venn) 加载R包 library…...
Linux中POSIX应用场景
Linux 提供了丰富的 POSIX(Portable Operating System Interface)标准接口,这些接口可以帮助开发者编写可移植、高效的应用程序。POSIX 标准定义了一系列系统调用和库函数,涵盖了文件操作、进程管理、线程管理、信号处理、同步机制…...
量子算法导论
重学了量子算法,不知道是温故而知新,还是之前的教材没有讲过这个概念。 如果把(图灵机)计算机比作一个查询机器,输入x通过f(x)作用得出结果,而查询的过程就是计算的过程。 中文解释…...
nasm - BasicWindow_64
文章目录 nasm - BasicWindow_64概述笔记my_build.batnasm_main.asmEND nasm - BasicWindow_64 概述 学习网上找到的demo. x64和x86的汇编源码还差挺多的。 x64的汇编代码不好写,细节整不对,程序就不运行。 如果要查为啥不运行,也要看和正向…...
SpringBoot:SSL证书部署+SpringBoot实现HTTPS安全访问
一、前言 SSL协议介于TCP/IP协议栈的第四层(传输层)和第七层(应用层)之间,为基于TCP的应用层协议(如HTTP)提供安全连接。它通过在客户端和服务器之间建立一个加密的通道,确保数据在传…...
selenium爬取苏宁易购平台某产品的评论
目录 selenium的介绍 1、 selenium是什么? 2、selenium的工作原理 3、如何使用selenium? webdriver浏览器驱动设置 关键步骤 代码 运行结果 注意事项 selenium的介绍 1、 selenium是什么? 用于Web应用程序测试的工具。可以驱动浏览…...
Spark提交任务
1、Spark提交任务到Yarn 1.1、DwKuduApp spark-submit --class com.io.etl.dwkudu.DwKuduApp \ --files /etl/etl-dwkudu/conf/doris.property,/etl/etl-dwkudu/conf/redis.property,/etl/etl-dwkudu/conf/log4j.property \ --master yarn --deploy-mode cluster \ --driver-…...
游戏引擎学习第113天
仓库:https://gitee.com/mrxiao_com/2d_game_2 黑板:优化的基本过程 在游戏编程中,优化是一个非常重要的学习内容,尤其是想要成为专业开发者时。优化的核心是理解代码的执行速度,以及如何提升其性能。在这个阶段,已经…...
token是什么
在自然语言处理(NLP)和机器学习的背景下,token 是指模型在处理文本时的最小单位。通常,这个单位可以是单词、字符,或者词的一部分。具体来说,token 的定义取决于你使用的模型和它的分词方式。 举个例子&am…...
23. AI-大语言模型-DeepSeek赋能开发-Spring AI集成
文章目录 前言一、Spring AI 集成 DeepSeek1. 开发AI程序2. DeepSeek 大模型3. 集成 DeepSeek 大模型1. 接入前准备2. 引入依赖3. 工程配置4. 调用示例5. 小结 4. 集成第三方平台(已集成 DeepSeek 大模型)1. 接入前准备2. POM依赖3. 工程配置4. 调用示例…...
IPv6报头40字节具体怎么分配的?
目录 IPv6报头结构 字段详解 示例代码:IPv6报头的Python实现 输出示例 IPv6协议是为了解决IPv4地址耗尽问题而设计的下一代互联网协议。与IPv4相比,IPv6不仅提供了更大的地址空间,还简化了报头结构,提高了网络设备的处理效率。…...
驱动开发、移植
一、任务明确:把创龙MX8的驱动 按照我们的要求 然后移植到 我们的板子 1.Linux系统启动卡制作, sd卡 先按照 《用户手册—3-2-Linux系统启动卡制作及系统固化》 把创龙的Linux系统刷进去。 2. 把TLIMX8-EVM的板子过一遍 把刚刚烧好系统的sd卡插入 创…...
BFS与Flood Fill:算法原理、实现细节与复杂度分析
目录 1. 概述 2. BFS 的基本原理 3. Flood Fill 算法 4. BFS 实现 Flood Fill 的步骤 5. C 实现 6. 代码解析 7. 复杂度分析 8. 应用场景 总结 1. 概述 Flood Fill 算法是一种用于填充封闭区域的算法,常用于图像处理、绘图工具和游戏开发中。BFS(…...
计算机网络基础杂谈(局域网、ip、子网掩码、网关、DNS)
目录 1. 简单局域网的构成 2. IP 地址 3. 子网掩码 4. IP地址详解自定义IP 5. IP 地址详解 6. 网关 7. DNS 域名解析 8. ping 1. 简单局域网的构成 交换机是组建局域网最重要的设备,换句话说,没有交换机就没法搭建局域网 交换机不能让局域网连…...
雷龙CS SD NAND(贴片式TF卡)测评体验
一、产品概述 近期获赠雷龙科技(Longsto)推出的CS系列贴片式SD NAND存储解决方案,包含两片工业级贴片式NAND芯片(CSNP16GCR01-AOW)及全兼容转接板。该方案支持TF卡形态扩展,实现高可靠性嵌入式存储应用。 …...
【Alertmanager】alertmanager告警系统原理剖析与应用实战,应有尽有非常全面
✨✨ 欢迎大家来到景天科技苑✨✨ 🎈🎈 养成好习惯,先赞后看哦~🎈🎈 🏆 作者简介:景天科技苑 🏆《头衔》:大厂架构师,华为云开发者社区专家博主,阿里云开发者社区专家博主,CSDN全栈领域优质创作者,掘金优秀博主,51CTO博客专家等。 🏆《博客》:Python全…...
Java——权限修饰符
一、权限修饰符的继承访问规则 以下按访问范围从宽到窄排序: 修饰符同包同类同包子类同包非子类跨包子类跨包非子类public✔️✔️✔️✔️✔️protected✔️✔️✔️✔️❌默认(包级)✔️✔️✔️❌❌private✔️❌❌❌❌ 关键点…...
一周学会Flask3 Python Web开发-redirect重定向
锋哥原创的Flask3 Python Web开发 Flask3视频教程: 2025版 Flask3 Python web开发 视频教程(无废话版) 玩命更新中~_哔哩哔哩_bilibili 前面我们学过渲染到模板页面,这个其实是一种内部的转发,浏览器地址栏地址没有变化。如果我们想重定向…...
python面向对象:方法
1. 实例方法 实例方法用于操作实例变量,必须包含 self 参数。 class Person:def __init__(self, name):self.name namedef greet(self):print(f"Hello, my name is {self.name}")person1 Person("Alice") person1.greet() # 输出ÿ…...
eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...
中南大学无人机智能体的全面评估!BEDI:用于评估无人机上具身智能体的综合性基准测试
作者:Mingning Guo, Mengwei Wu, Jiarun He, Shaoxian Li, Haifeng Li, Chao Tao单位:中南大学地球科学与信息物理学院论文标题:BEDI: A Comprehensive Benchmark for Evaluating Embodied Agents on UAVs论文链接:https://arxiv.…...
前端倒计时误差!
提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...
【SQL学习笔记1】增删改查+多表连接全解析(内附SQL免费在线练习工具)
可以使用Sqliteviz这个网站免费编写sql语句,它能够让用户直接在浏览器内练习SQL的语法,不需要安装任何软件。 链接如下: sqliteviz 注意: 在转写SQL语法时,关键字之间有一个特定的顺序,这个顺序会影响到…...
全面解析各类VPN技术:GRE、IPsec、L2TP、SSL与MPLS VPN对比
目录 引言 VPN技术概述 GRE VPN 3.1 GRE封装结构 3.2 GRE的应用场景 GRE over IPsec 4.1 GRE over IPsec封装结构 4.2 为什么使用GRE over IPsec? IPsec VPN 5.1 IPsec传输模式(Transport Mode) 5.2 IPsec隧道模式(Tunne…...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...
【7色560页】职场可视化逻辑图高级数据分析PPT模版
7种色调职场工作汇报PPT,橙蓝、黑红、红蓝、蓝橙灰、浅蓝、浅绿、深蓝七种色调模版 【7色560页】职场可视化逻辑图高级数据分析PPT模版:职场可视化逻辑图分析PPT模版https://pan.quark.cn/s/78aeabbd92d1...
在 Spring Boot 中使用 JSP
jsp? 好多年没用了。重新整一下 还费了点时间,记录一下。 项目结构: pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...
