刷题笔记系列
文章目录
- 刷题笔记系列
- 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
总结
- 链表多画图辅助理解。
- 修改链表的题目善用虚拟头节点和“指针”。捋清楚关键节点(断点、连接点、待处理部分的头)和处理过程。
- 快慢指针也是很常用的思路。