代码随想录第三天| 203.移除链表元素、707.设计链表、206.反转链表
第一题
- 其实这题我一直不理解为什么明明整个过程没有操作dummy,最后要返回dummy.next,
- dedug了一下发现是pre.next = cur.next;这一步的时候对dummy进行了更新,
- 之后又看了看动画演示,明白具体的操作,但还是不理解原因
- 结果我就发现我虽然不理解,但是我已经背下来了,就默写下来,看看能不能通过之后的题目再加深对链表的理解
二刷来解决一刷留下的问题
为了便于说明和理解,先定义一个简单的类:
public class Solution {
private int a;
private int b;
public void setA(int a) {
this.a = a;
}
public void setB(int b) {
this.b = b;
}
}
再讲对象的赋值与引用之前,我们先给出一个让很多人迷惑的现象:
Solution s1 = new Solution();
s1.setA(1);
s1.setB(2);
Solution s2 = s1;
s2.setA(-1);
我们定义一个Solution类的对象s1,并给其成员变量赋值。然后再定义另一个对象s2,并令s2=s1,如果我现在令s2中的a=-1,现在s1和s2中的a的值会是多少呢?
对于Solution s1 = new Solution();这条语句,这条语句执行的动作是创建一个对象,但是它确包含了四个步骤:
-
右边“new Solution”,表示以Solution类为模板,在堆空间中创建一个Solution类对象;
-
“()”,这对括号表示,在对象创建后,会立马调用Solution类的构造函数,由于没有给参数,所以会调用默认的无参构造。
-
左边的“Solution s1 ”创建了一个Solution类的引用变量,也就是说用来指向Solution对象的对象引用。这和C语言中的指针可以理解为一个意思。
-
“=”,这个等号操作符表示使对象引用s1指向刚创建的Solution对象。
所以,这条语句包含了两个实体:一个是对象引用变量,一个是对象本身。
为了形象地说明对象、引用及它们之间的关系,可以做一个比喻:
A有一栋房子,他把地址告诉了B,B就知道了A家的地址,然后B又把A家的地址告诉了C。此时,B和C都知道了A家的地址,但是这栋房子确是属于A的。如果此时,B去A家拿了一个东西,然后C去了后会发现少了一样东西。所以B和C只是知道A家的地址,可以随时访问,但是确不是独有的。
所以,我们有以下结论:
(1)一个对象引用可以指向0个或1个对象(一个人可以知道房子的地址,也可以不知道);
(2)一个对象可以有N个引用指向它(可以有N个人知道房子的地址)。
Java对象和引用的关系可以说是互相关联,却又彼此独立。彼此独立主要表现在:引用是可以改变的,它可以指向别的对象。
从存储空间上来说,对象和引用也是独立的,它们存储在不同的地方,对象一般存储在堆中,而引用存储在速度更快的栈中。
引用可以指向不同的对象,对象也可以被多个引用操纵。如上面说的s1和s2都指向了同一个对象。既然两个引用指向同一个对象,那么不管使用哪个引用操纵对象,对象的内容都发生改变,并且只有一份,通过s1和s2得到的内容自然也一样。
再理解了对象和引用的关系后,再来看参数传递。
Java只有一种参数传递方式:那就是按值传递,即Java中传递任何东西都是传值。如果传入方法的是基本类型的东西,你就得到此基本类型的一份拷贝。如果是传递引用,就得到引用的拷贝。
第二题
class MyLinkedList {
class Listnode {
int val;
Listnode next;
public Listnode() {
}
public Listnode(int val) {
this.val = val;
}
}
int size;//链表的大小
Listnode head;//虚拟头结点
public MyLinkedList() {// 初始化链表
size = 0;
head = new Listnode(0);
}
//获取链表中第 index 个节点的值。如果索引无效,则返回-1
public int get(int index) {
if (index < 0 || index >= size) {//索引无效
return -1;
}
Listnode cur = head;//定义遍历链表的指针
for (int i = 0; i <= index; i++) {
cur = cur.next;
}
return cur.val;
}
//在链表的第一个元素之前添加一个值为 val 的节点。插入后,新节点将成为链表的第一个节点
public void addAtHead(int val) {
Listnode newNode = new Listnode(val);
newNode.next = head.next;//让新结点指向原头结点
head.next = newNode;//虚拟结点指向头结点
size++;
}
//将值为 val 的节点追加到链表的最后一个元素
public void addAtTail(int val) {
Listnode newNode = new Listnode(val);
Listnode cur = head;//定义遍历链表的指针
while (cur.next!=null){
cur = cur.next;//此时current指向最末的一个结点
}
cur.next = newNode;//最末的一个结点指向新结点
size++;
}
/*
* 在链表中的第 index 个节点之前添加值为 val 的节点
* 如果 index 等于链表的长度,则该节点将附加到链表的末尾
* 如果 index 大于链表长度,则不会插入节点
* 如果 index 小于0,则在头部插入节点
*/
public void addAtIndex(int index, int val) {
if(index>size){//如果 index 大于链表长度,则不会插入节点
return;
}else if(index<0){//如果 index 小于0,则在头部插入节点
addAtHead(val);
}else if(index==size){
addAtTail(val);//如果 index 等于链表的长度,则该节点将附加到链表的末尾
}else {
Listnode newNode = new Listnode(val);//要加入的这个结点
Listnode cur = head;//定义遍历链表的指针
for (int i = 0; i < index; i++) {
cur = cur.next;
}
newNode.next = cur.next;
cur.next = newNode;
size++;
}
}
//如果索引 index 有效,则删除链表中的第 index 个节点
public void deleteAtIndex(int index) {
if (index < 0 || index >= size) {//索引无效
return ;
}
Listnode cur = head;//定义遍历链表的指针
for (int i = 0; i < index; i++) {
cur = cur.next;
}
cur.next = cur.next.next;
size--;
}
}
- 也早上看的思路,晚上扣了半天,看了视频,最后发现其实逻辑还挺清晰,就是第一次看到的时候有点懵
- 太神奇了,竟然一遍过,甚至都没debug,这么长都没有报错,而且中间我还改了点东西,出息了出息了
- 就希望下次再遇到的时候能做出来吧
第三题
- 这题我是早上看的思路,吃完晚饭实现的,思路比较清晰简单
- 但是在实现的时候,返回值还是debug了一下才发现是pre,我本来写的是head,说到底还是对链表的结构理解不到位
总结
- 本来对链表就比较陌生,之前也从没用链表做过题,接触下来发现还可以,没有之前以为的那么难
- 但是今天的题没有一道是自己做出来的,都是看完题解后写的,方法也都是单链表
- 感觉今天还是有进步的,起码可以理解并用其他的方式解链表题了
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?