203.移除链表元素
🙋遇到的问题
- 删除节点后又一次犯了“指针后移”的错误❌。
- 记住:不删节点才后移,删除指针先不动
- 为什么:因为cur指针指向的是要删除节点的前一个节点,如果删除节点后马上指针后移,那么会跳过删除节点的后一个节点,万一这个节点正好是要删除的,那么逻辑就是有问题的
2. cur指针的初始值是dummyHead还是head?
- 是dummyHead
- 为什么:如果要删除的是head节点,那么就必须找到head的前一个节点才能删。那么head的前一个节点怎么找呢?就是dummyHead。让cur=dummyHead就是找到要删除节点的前一个节点。
- 记住:增删节点都要让指针指向待删除/增加的前一个节点
707.设计链表
🙋遇到的问题
1. 怎么定义MyLinkedList()? 这个题目是什么意思?
题目意思是定义一个ListNode()类的链表,有int val和ListNode next属性
再定义一个MyLinkedList类,用于给listNode 链表增删改查
MyLinkedList类有int size和ListNode dummyHead属性
2. head节点怎么表示?怎么表示dummyHead.next的逻辑?/怎么在方法体中让节点连接起来?
不用new一个head节点。head节点可以理解是在index=0处增加一个新节点
在addAtIndex()方法中,new一个新节点node,再增加节点(如以下),就可以将一个个节点连接起来:
node.next = cur.next;
cur.next = node;
3. 每一个方法中,cur的初始值是dummyHead, 还是head?
原则上来说增删是dummyHead, 只查是head,为了统一可以都用dummyHead,
如果查的时候cur起始值是dummyHead,就要注意:
最后返回的是cur.next。为什么?
因为cur从dummyHead开始的话,循环结束时,cur指向的index处的前一个节点,而我们要查找的是index处的节点,所以要返回cur的下一个节点,即cur.next
4. addAtTail()方法中循环结束条件是while(cur.next != null)还是while(cur != null)
是cur.next != null
因为cur指向的是要增加节点处的前一个节点
5. 参考文章中为什么给MylinkedList定义size和ListNode dummyHead属性?
不是很懂,但是现在的理解是每一个方法中都需要size和dummyHead,相当于这两个属性是所有方法的共同点
💡新的想法
- 要增删找前任,只查找找当前
- 增加节点:
- 要么先连后再连前
- 要么temp保存好下一个节点,先连前再连后
- 这道题非常考察细节,看清题意index要求,处理好边界条件
- Index <0 || index > size: 返回-1
- Index = 0: 在头节点增加/删除
- Index= size: 在尾部增加
206.反转链表
🙋遇到的问题
1. 要不要用虚拟头节点?
不需要。
- 这道题再次出错的原因是:定义了虚拟头节点,让pre=dummyHead, dummyHead.next = head, cur=head,结果是链表反转后出现circle.
- 为什么定义了虚拟头节点会出现circle?
因为在第一次反转中,让cur.next=pre,此时pre=dummyHead,而dummyHead.next = cur(即pre.next = cur), 所以就出现了: cur-->pre-->cur
- 怎么解决?
不用定义虚拟头节点,让pre=null, cur=head,其他逻辑不变
💡新的想法
1. ⚠️⚠️⚠️链表反转前要保存当前指针的下一个节点
2. 双指针的先后移动顺序也会影响逻辑。这道题中要先移动pre指针,再移动cur指针。
为什么?
因为先移动cur的话,让cur=cur.next,cur的指向已被改变,再让pre=cur,实际pre指向的并不是cur,而是cur.next
3. 虚拟头节点的作用是简化链表的边界条件处理。链表反转中只要将pre=null,就可以将头节点变成尾节点,虚拟头节点并不是必须的。虚拟头节点在以下情况下使用:
- 增删节点。简化头节点的特殊处理步骤