203 移除链表元素
203. 移除链表元素https://leetcode.cn/problems/remove-linked-list-elements/
题目描述:
给你一个链表的头节点 head
和一个整数 val
,请你删除链表中所有满足 Node.val == val
的节点,并返回 新的头节点 。
解题思路:
链表的题,一般加一个虚拟头结点dummy head会更好处理,即处理规则会比较统一。否则有时候要分是否为头结点进行讨论。本题采用加dummy head的方法。
用一个指针cur去遍历链表。由于我们删除链表结点时,要拿到该结点的上一个结点,让其指向下下一个结点,就可以删除该结点。所以此处cur的含义是:我们删除结点的上一个结点。
Java小知识:
1. Java里不是“->”,而是“.”。
2. 创建ListNode的时候,力扣给出的代码是直接用了int,实际上还可以用泛型<T>,可惜我忘了,到时候要补一下泛型的知识。
放一下易错的地方:
不写else的话,如果连着两个结点的值都等于val,会有一个被忽略。
力扣代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode dummyHead = new ListNode(val-1,head);
ListNode cur = dummyHead;//临时指针,指向要删的结点的前一个
while(cur.next != null){
if(cur.next.val == val) cur.next = cur.next.next;
else cur = cur.next;
}
return dummyHead.next;
}
}
本地代码:
我在本地debug,用了两个类,放在同一个package下。
Solution类:
在main函数中,手动创建了链表。l3和l33的值都为3。
package lc203;
class Solution {
public ListNode removeElements(ListNode head, int val) {
ListNode dummyHead = new ListNode(val-1,head);
ListNode cur = dummyHead;//临时指针,指向要删的结点的前一个
while(cur.next != null){
if(cur.next.val == val) cur.next = cur.next.next;
else cur = cur.next;
}
return dummyHead.next;
}
public static void main(String[] args) {
Solution solution = new Solution();
//创建一个链表,头结点是l1,共5个结点。
ListNode l5 = new ListNode(5,null);
ListNode l4 = new ListNode(4,l5);
ListNode l3 = new ListNode(3,l4);
ListNode l33 = new ListNode(3,l3);
ListNode l2 = new ListNode(2,l33);
ListNode l1 = new ListNode(1,l2);
ListNode cur = l1;
while(cur != null){//cur是要打印的结点,和之前函数中的cur意义不一样
System.out.print(cur.val);
cur = cur.next;
}
solution.removeElements(l1,3);
ListNode cur2 = l1;
while (cur2 != null){
System.out.print(cur2.val);
cur2 = cur2.next;
}
}
}
ListNode类:
直接复制力扣上的描述
package lc203;
//Definition for singly-linked list.
public class ListNode {
int val;
ListNode next;
ListNode() {}
ListNode(int val) { this.val = val; }
ListNode(int val, ListNode next) { this.val = val; this.next = next; }
}
206 反转链表
206. 反转链表https://leetcode.cn/problems/reverse-linked-list/
题目描述:
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
解题思路:
这题很折磨,缝缝补补改来改去好几次。
我的思路是这样的,相当于三个指针在动,核心是让cur指向pre,tmp是用于暂存后面结点的,以免丢失后面的结点。如图所示,带有圆圈的1 2 3 4是步骤顺序。
要注意的点:
1. while循环的进入条件,是cur!=null,之前想写cur.next!=null,你就想cur为最后一个结点时,进不进循环,实际是要进的,因为最后一个结点还没指向倒数第二个结点呢,所以不能这么写。还有就是,可以写成while(cur)这样更简洁。
1个月后更新:Java里好像不能这样写,会报错
2. 初始化条件要注意,我之前初始化写的是:
ListNode pre = head;
ListNode cur = head.next;//不能这样写,可能会是空链表。。。
ListNode tmp = cur.next;
这样就没考虑到传进来的链表可能是空链表的情况,head就是null,head没有next,更没有head.next.next。
所以我改成了这样,相当于三个指针一起往前挪了一位。
ListNode pre = null;
ListNode cur = head;
ListNode tmp = cur.next;
结果还是错的,因为tmp还是cur.next,说了一百遍cur可能没有next。
所以要把tmp放在循环里面去定义,因为进循环首先就要判断cur是否为空,这样就可以避免空指针异常。
3. (也算是初始化问题吧)还要说的就是,反转链表不是从第二个结点指向第一个结点开始,而是从第一个结点指向null开始。为什么呢?因为如果你从第二个结点开始,那相当于第一个结点的next还是第二个结点,那么假设原链表是12345,你按反转顺序打印本该是54321,结果你的第一个结点没处理,打印出来就是54321212121212121212121无限循环。这个第一个结点,反转后相当于尾结点,请记住,尾结点的next是null!
力扣代码:
class Solution {
public ListNode reverseList(ListNode head) {
ListNode pre = null;
ListNode cur = head;
while(cur!=null){//之前想写cur.next!=null,你就想最后一个结点进不进循环,实际是要进的,所以不能这么写
ListNode tmp = cur.next;
cur.next = pre;
pre = cur;
cur = tmp;
if(tmp!=null) tmp = tmp.next;
}
return pre;//返回尾结点
}
}
24 两两交换链表中的节点
题目描述:
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
解题思路:
先分情况讨论:链表偶数个节点的话,两两交换;链表奇数个节点的话,最后一个节点不用管。
操作的指针(cur)一定要指向要反转的2个节点的前1个节点。
循环终止条件为(cur.next!=null && cur.next.next!=null),相当于cur后面必须要有至少两个节点。
力扣代码:
搞了三个tmp,懒得想到底哪个节点要暂存
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode swapPairs(ListNode head) {
ListNode dummyHead = new ListNode(-1,head);
ListNode cur = dummyHead;
while(cur.next!=null && cur.next.next!=null){
//不管了直接搞三个tmp,就不用想哪个要暂存 哪个不用暂存了
ListNode tmp1 = cur.next;
ListNode tmp2 = cur.next.next;
ListNode tmp3 = cur.next.next.next;
cur.next = tmp2;
tmp2.next = tmp1;
tmp1.next = tmp3;
cur = cur.next.next;
}
return dummyHead.next;
}
}
19 删除链表的倒数第N个节点
19. 删除链表的倒数第 N 个结点https://leetcode.cn/problems/remove-nth-node-from-end-of-list/
题目描述:
给你一个链表,删除链表的倒数第 n
个结点,并且返回链表的头结点。
解题思路:
快慢指针,fast领先n+1个,因为slow要指向要删除节点的前一个。
(自己随便找个例子,简单画个图就知道到底领先几个了)
力扣代码:
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
ListNode dummyHead = new ListNode(-1,head);
ListNode fast = dummyHead;
ListNode slow = dummyHead;
for(int i=0;i<n+1;i++){//fast先走n+1步
fast = fast.next;
}
while(fast!=null){
fast = fast.next;
slow = slow.next;
}
slow.next = slow.next.next;
return dummyHead.next;
}
}
142 环形链表2
. - 力扣(LeetCode). - 备战技术面试?力扣提供海量技术面试资源,帮助你高效提升编程技能,轻松拿下世界 IT 名企 Dream Offer。https://leetcode.cn/problems/linked-list-cycle-ii题目描述:
给定一个链表的头节点 head
,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos
是 -1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
解题思路:
快慢指针,如图所示,fast走的路程为x+y+n(y+z),slow走的路程为x+y。
要注意的点:
1. 为什么快慢指针一定会相遇,而不是快指针跳过慢指针?入环后,相当于fast在以每次一个节点的速度靠近慢指针,所以一定会相遇。
2. 为什么慢指针一定不到一圈?慢指针进入环的时候,两个指针相差的节点数一定小于一圈,相对速度是1,所以一圈内必被追上。
3. 如下图,懒得打字了
4. 写代码的时候,我犯了一个错误,看下面代码注释掉的那行,我在while(index1!=index2)的里面写了if(index1==index2) return xxx;这样不行,因为有可能一开始index1就与index2相等,进不了while,到不了if,就永远出不了外层循环。
力扣代码:
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast = head;
ListNode slow = head;
while(fast!=null && fast.next!=null){
fast = fast.next.next;
slow = slow.next;
if(fast==slow){
ListNode index1 = slow;//快慢指针相遇点
ListNode index2 = head;//起点
while(index1!=index2){//这俩指针同时出发,在入环第一个节点相遇
index1 = index1.next;
index2 = index2.next;
// if(index1 == index2) return index1;//为什么不能这样写:可能一开始index1就和index2相等,进不了这个while循环,就出不了外面的大循环
}
return index1;
}
}
return null;
}
}