刷题笔记系列
文章目录
- 刷题笔记系列
- 1. LeetCode 203 移除链表元素
- 2. LeetCode 707 设计链表
- [3. LeetCode 206 反转链表](https://leetcode.cn/problems/reverse-linked-list/description/)
- [3.1 LeetCode 92 反转链表II](https://leetcode.cn/problems/reverse-linked-list-ii/description/)
- 4. LeetCode 24 两两交换链表中的节点
- 5. LeetCode 19 删除链表倒数第N个节点
- [5.1 LeetCode 61 旋转链表](https://leetcode.cn/problems/rotate-list/description/)
- 6. LeetCode 面试题 02.07. 链表相交
- 7. LeetCode 142. 环形链表
- 总结
1. LeetCode 203 移除链表元素
给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val == val 的节点,并返回 新的头节点 。

我一开始做的时候,又犯了数组移除元素用双指针做的那个错,判断到符合条件的元素后,把指针后移了,却没有考虑指针新移到的那个位置符不符合标准,从而漏掉了一些元素的判断。
一开始圈出来的那里没有用while,用的if,这就导致[7,7,7,1,1,7]这种存在连
续相同元素的测试用例过不了。因为第一个7判断符合后,q = q.next使指针直接移到了下一个7,然后下一轮while循环开始,直接比较这个7的下一个7了……而圈出来那里用while,只要有连续的val,就会先从链表中卸下val,直到下一个值不为val,这时把q指针移到下一个就合理了。
但上面这种解法不好,看着很绕
直接仿照数组移动元素,是我需要的值才放进去,不是我需要的值就移指针,这样写好理解多了。
public class Solution {
public ListNode RemoveElements(ListNode head, int val) {
ListNode newLink = new ListNode();
ListNode newTail,cur;
newTail = newLink;
cur = head;
while(cur != null){
if(cur.val != val){
newTail.next = cur;
newTail = cur;
cur = cur.next;
newTail.next = null;
}else{
cur = cur.next;
}
}
return newLink.next;
}
}
2. LeetCode 707 设计链表
加头节点的好处:不用特意处理链表元素为空这种特殊情况;插入节点方便
加链表长度的好处:方便判空,但是添加和删除元素时一定要记得修改值
3. LeetCode 206 反转链表
有三个方法,三指针法、虚拟头节点法、递归法;递归法的思路和三指针法是类似的
虚拟头节点法是利头插法会倒序的原理模拟插入节点,头指针一直指向虚拟头节点,另两个指针在原链表不停移动。
三指针法则是三个指针都要移动,但是所指向节点的next值不用那么频繁地变化。
不要被链表图形像绳索一样的暗示束缚住,并不需一直维持这跟绳索,它是可拆卸的,可以一节节拆下拼起。
其实本质上虚拟头结点法和三指针法是一样的,只是新建链表类型不一样,一个是有虚拟头节点的,一个是没有虚拟头节点的。这就造成了一个要改节点指向,一个要移动指针。
三指针法
public class Solution {
public ListNode ReverseList(ListNode head) {
ListNode cur,temp,newListPre;
newListPre = null;
cur = head;
while(cur != null){
temp = cur.next;
cur.next = newListPre;
newListPre = cur;
cur = temp;
}
return newListPre;
}
}
递归法
public class Solution {
public ListNode ReverseList(ListNode head) {
return Reverse(null, head);
}
public ListNode Reverse(ListNode pre, ListNode cur){
if(cur == null)
return pre;
ListNode next = cur.next;
cur.next = pre;
return Reverse(cur, next);
}
}
递归法实际上是利用栈递归完成迭代,
每次递归中记录下个待处理节点(next = cur.next) 并处理当前节点转向(cur.next = pre)
然后把处理结果(pre) 和 待处理链表(next)继续传入方法进行递归处理
递归中灵活传递入参也是个值得探讨的问题,汉诺塔和一些回溯就是经典例子。
3.1 LeetCode 92 反转链表II
给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
- 思路:找到关键节点left、right等,反转left-right范围的节点,拼接
- 代码
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
if(left == right || head.next == null)
return head;
ListNode list1End = findNode(head, left - 1);//第一段 head - list1End 或为 null
ListNode list3Start = findNode(head, right + 1);// 第三段 list3Start - 链表尾 或为null
ListNode list2Start = findNode(head, left); //第二段 list2Start开始
ListNode cur = list2Start.next;
ListNode p = null;
list2Start.next = null;
while( cur != list3Start){
p = cur.next;
cur.next = list2Start;
list2Start = cur;
cur = p;
}
if(list3Start != null){
ListNode list2End = findNode(list2Start, right-left+1);
list2End.next = list3Start;
}
if(list1End != null){
list1End.next = list2Start;
return head;
}else{
return list2Start;
}
}
public ListNode findNode(ListNode head, int index){
if(index < 1)
return null;
ListNode p = head;
while( --index > 0 && p!=null){
p = p.next;
}
return index > 0 ? null : p ;
}
}
4. LeetCode 24 两两交换链表中的节点
这题不要想得太复杂,其实就是需要按一定的顺序移链表指向而已。
可以画图辅助判断,图的链接怎么改,代码就按这个来。只不过要注意移动顺序。
运用虚拟头节点法要注意的是,要记录下虚拟头结点,不然cur移着移着链表就找不到了。
public class Solution {
public ListNode SwapPairs(ListNode head) {
ListNode dumyHead = new ListNode();
dumyHead.next = head;
ListNode cur,p,q;
cur = dumyHead;
p = cur.next;
while(p != null && p.next != null){
q = p.next;
p.next = q.next;
q.next = p;
cur.next = q;
cur = p;
p = p.next;
}
return dumyHead.next;
}
}
5. LeetCode 19 删除链表倒数第N个节点
思路:快慢双指针+虚拟头节点
将链表挂在虚拟头节点后。
先使快慢指针相差n,也即快指针移动n次 (注意两指针相差n说的是index之差,不是说两指针间间隔n个节点),然后同时移动快慢指针直至快指针到达链表最后一个节点。此时慢指针就在待删除节点的前面。
此时


5.1 LeetCode 61 旋转链表

优化:
其实用不到快慢指针。
第一次遍历计算链表长度时可以用 p.next做条件,最后p停在表尾。
算出移动次数c后 指针cur移动到断点前一位
最后表尾连表头,p.next = head ; 标记新头head = cur.next; 断出新尾 cur.next = null就行了
6. LeetCode 面试题 02.07. 链表相交
思路:这题和 5 类似,也是用快慢指针。只不过这题要先遍历链表把两链表的长度差n求出来,然后快指针移动n次到达比较的开始位置,快慢指针同时在两链表中遍历比较。

7. LeetCode 142. 环形链表
思路:运用快慢指针+数学推理。细节比较多,常看常新。
代码随想录-环形链表II

总结
- 链表多画图辅助理解。
- 修改链表的题目善用虚拟头节点和“指针”。捋清楚关键节点(断点、连接点、待处理部分的头)和处理过程。
- 快慢指针也是很常用的思路。
这篇刷题笔记主要涉及LeetCode中的链表问题,包括移除链表元素、设计链表、反转链表及其变种,使用了双指针、虚拟头节点等技巧。重点讲解了如何避免在处理链表时的常见错误以及如何有效地修改链表结构。
515





