目录
24 两两交换链表中的节点
题目链接
题目要求
给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)
思路分析
以1→2→3→4为例
- 我很早之前做这个题目,想的是1→2,变成2→1 然后3→4,变成4→3,但是这种想法,很难去实现,因为没有固定的“变化”,即没有规律。当2→1 完成后,链表是2→1→3→4,后续很难处理。没有彻底掌握
- 2022年9月24日19:01:30 再写这道题目时,首先我想到了需要加一个虚拟节点。一部分是因为:需要返回新的头结点,有虚拟节点的话方便操作,另一部分是:加上虚拟节点之-1→1→2→3→4。 三个节点为一个周期,第一个节点不变,交换后面两个交点,-1→2→1→3→4,然后temp 向后移动两个位置。再三个节点为周期。直到不需要交换。
- while()循环的判定条件,java中要特别注意:NullPointer 空指针异常。由2可得:三个节点为周期,所以三个节点都要存在,如果最后之后只剩两个节点时就不需要交换。while(temp.next.next!=null){}.此时存在空指针异常的可能性,所以这里的while()循环条件要写的全面。
此处引用公众号“代码随想录”的图片
代码如下
class Solution {
public ListNode swapPairs(ListNode head) {
// 先判断链表是否为空
if(head==null){
return head;
}
else{
// 生成一个虚拟头结点
ListNode dummy=new ListNode(-1,head);
ListNode temp=dummy;
ListNode cur=null;
ListNode curNext=null;
while (temp!=null&&temp.next!=null&&temp.next.next!=null) { // while() 循环可能没写对
cur=temp.next;
curNext=cur.next;
temp.next=curNext;
cur.next=curNext.next;
curNext.next=cur;
// 三个节点为一个周期,所以temp 移动两个位置
temp=cur;
}
head =dummy.next;
return head;
}
}
}
本题收获
- 自己解决问题的能力。我能把三个节点为周期,实现交换的目的想明白,但是卡在了while()循环的判定条件,我第一次写while(temp,next.next!=null),自己也知道一定会发生nullpointer 异常,最开始不知道如何去改,当下有想过去看答案怎么写,但是不太想轻易放弃,后面又认真琢磨了以下,决定把while()循环条件写的全面一点,给了自己最后10min时间思考,避免死磕。
- 注意细节问题
19.删除链表的倒数第N个节点
题目链接
19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)
题目要求
给你一个链表,删除链表的倒数第 n
个结点,并且返回链表的头结点。
- 链表中结点的数目为
sz
1 <= sz <= 30
0 <= Node.val <= 100
1 <= n <= sz
思路分析
- 暴力解法,两遍for循环,第一次先遍历出整个链表的长度length,第二遍去寻找length-n的节点,这样操作的时间复杂度是O(n^2),这是我很早之前第一遍的做法。
- 在2022年9月28日晚上,我回忆起来 :代码随想录上的思路“快慢指针”。让快指针比慢指针先走n步,这样当fast 走到链表的末尾,慢指针刚好指向被删除节点的前驱。
- 关于细节问题,while()循环中的条件怎么写?
- 我的建议是以题目中的示例1→2→3→4→5 为例,n=2,自己手动模拟一遍。
- 以及认真看题目的提示。这里面包含了我应该如何处理一些特殊问题,在这个问题中我不需要判断链表是否为空,因为
1 <= sz <= 30
代码如下
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
// 需要创建一个虚拟节点,万一删除操作之后,头结点发生了变化
ListNode dummy=new ListNode (-1,head);
// 使用快慢指针法,让快指针比慢指针先走n步,这样当快指针到达链表的末尾元素时,
// 慢指针刚好指向被删除节点的前驱结点。
ListNode slow=dummy;
ListNode fast=dummy;
while (n>0){
fast=fast.next;
n--;
}
while(fast.next!=null){
fast=fast.next;
slow=slow.next;
}
// 此时,慢指针slow指向被删除节点的前驱元素
slow.next=slow.next.next;
head=dummy.next;
return head;
}
}
本题收获
- 开心自己能在二刷这个题目的时候,想到了“快慢指针法”,不再使用暴力解法
- 更加熟练的处理边界条件。给自己点赞。
面试题 02.07. 链表相交
题目链接
面试题 02.07. 链表相交 - 力扣(LeetCode)
题目要求
给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null 。
思路分析
- 这个图可以很直观的看出:当两个值val相等时,并不是相交的起始点,而是指向相同的节点要关注next值。
- “尾部对齐” :当两个链表相交时,从相交起始节点到链表的尾部是重合的,因此需要从两个不同的链表相同的位置开始遍历,寻找相交起始节点。
- 如何找到两个不同链表的同一位置开始遍历?
- 分别求出两个链表的长度,然后较长的链表-较短链表的长移动长链表的head
- 如何找到两个不同链表的同一位置开始遍历?
- 这个题目:两个链表可以是空链表,所以需要判断是否为空的特殊情况。
代码如下
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA==null || headB==null){
return null;
}
// 分别求出链表A、B的长度
int lengthA=0;
int lengthB=0;
ListNode tempA=headA;
ListNode tempB=headB;
while (tempA!=null){
lengthA++;
tempA=tempA.next;
}
while (tempB!=null){
lengthB++;
tempB=tempB.next;
}
// 从同一起点开始遍历
if(lengthA>lengthB){
int move_num=lengthA-lengthB;
while (move_num>0){
headA=headA.next;
move_num--;
}
}
else{
int move_num=lengthB-lengthA;
while (move_num>0){
headB=headB.next;
move_num--;
}
}
while (headA!=headB){
headA=headA.next;
headB=headB.next;
}
// 需要考虑不相交的情况
if(headA!=null){
return headA;
}
else {
return null;
}
}
}
本题收获
- 这个题目算是1.5刷,之前一刷的时候,这个题目连代码可能都没有看懂,大概只记得了一个“尾部对齐”的思路,这次顺着这个思路,大概想到了要求出链表的长度,然后再从不同链表的同一起点开始遍历。
- 考虑问题更加全面,当两个链表均不为空的时候,也存在不相交的可能,也要考虑进去。