1. 今日收获:两两交换链表中的节点,删除倒数第N个节点,链表相交,环形链表
2. 两两交换链表中的节点
题目链接:24. 两两交换链表中的节点
思想:使用虚拟头节点,当前指针始终指向要交换的两个节点的上一个节点,定义中间变量存储指针指向改变前的下一个节点
方法代码:
class Solution {
public ListNode swapPairs(ListNode head) {
ListNode dummyHead = new ListNode();
dummyHead.next= head;
ListNode cur=dummyHead; // // cur始终指向要交换的两个节点的上一个节点
while (cur.next!=null&&cur.next.next!=null){ // 对应节点为偶数和奇数的情况
ListNode temp1=cur.next;
ListNode temp2=cur.next.next.next;
// 交换节点
cur.next=cur.next.next;
cur.next.next=temp1;
temp1.next=temp2;
cur=temp1;
}
return dummyHead.next;
}
}
总结:
1. 要对链表进行修改或删除操作时,当前指针总是指向要修改节点的上一个节点。但是查询和修改,计算链表长度就可以直接指向目标节点。
2. 虚拟头节点的使用时机,关键是看需不需要对头节点和其他节点分情况讨论。如果需要分开讨论则使用虚拟头节点更加方便。
3. 对于需要频繁改变节点指向的问题,画图有助于理清思路,同时要注意用变量保存节点改变指向前的下一个节点,不然容易“掉链子”。
3. 删除链表的倒数第N个节点
题目链接:19. 删除倒数第N个节点 - 力扣(LeetCode)
思路:定义快慢指针寻找链表的倒数第N个节点。快指针先走n+1步,然后快慢指针一起移动,使慢指针指向目标节点的上一个节点
代码:
class Solution {
public ListNode removeNthFromEnd(ListNode head, int n) {
// 使用双指针寻找要删除的节点
ListNode dummyHead = new ListNode();
dummyHead.next=head;
ListNode fast=dummyHead;
ListNode slow=dummyHead; // slow指向要删除节点的上一个节点
while (fast!=null){
if (n>=0){ // fast先走n+1步
fast=fast.next;
n--;
}else {
fast=fast.next; // 两个指针一起移动
slow=slow.next;
}
}
slow.next=slow.next.next;
return dummyHead.next;
}
}
总结:这题快慢指针寻找目标节点的思路和下一题寻找链表相交点的思路比较像,都是用快指针把多出来的部分先消耗掉,然后快慢指针就可以平起平坐,一起愉快地玩耍了。
4. 链表相交
思路:先求出两个链表的长度,定义指针指向长链表中与短链表从右边开始长度一致的节点,然后两个指针同步移动,挨个判断当前节点是否相等。
方法代码:
public class Solution {
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
int lenA=0;
int lenB=0;
int sub=0;
ListNode curA=headA;
ListNode curB=headB;
// 求两个链表的长度和差值
while (curA!=null){
lenA++;
curA=curA.next;
}
while (curB!=null){
lenB++;
curB=curB.next;
}
curA=headA;
curB=headB;
// 长链表向后移动sub步,两个链表右对齐
if (lenA>=lenB){
sub=lenA-lenB;
while (sub>0){
curA=curA.next;
sub--;
}
}else{
sub=lenB-lenA;
while (sub>0){
curB=curB.next;
sub--;
}
}
// 开始同步移动并比较
while (curA!=null&&curB!=null){
if (curA==curB){
return curB;
}
curA=curA.next;
curB=curB.next;
}
return null;
}
}
总结:这道题和上一道题快慢指针的思想有些类似,都是其中一个指针先移动,然后两个指针再同步移动。
5. 环形链表Ⅱ
思想:定义快慢指针寻找其相遇点,快指针每次走两步,慢指针每次走一步。根据公式可以推导出,相遇点到环入口点和head到入口点的距离相等。然后再定义两个指针分别从head和相遇点出发同步移动,寻找环的入口点。
代码:
public class Solution {
public ListNode detectCycle(ListNode head) {
// 定义快慢指针,寻找相遇点
ListNode fast=head;
ListNode slow=head;
ListNode inter=null; // 相遇点
while (fast!=null&&fast.next!=null){
fast=fast.next.next; // 一次走两步
slow=slow.next;
if (fast==slow){ // 存在环
inter=fast;
break;
}
}
// 相遇点到环入口点和head到入口点的距离相等
ListNode temp1=head;
ListNode temp2=inter;
while (temp1!=null&&temp2!=null){
if (temp1==temp2){
return temp1;
}
temp1=temp1.next;
temp2=temp2.next;
}
return null;
}
}
总结:如果链表无环,慢指针永远追不上快指针;除非是链表有环,快指针在兜圈子;再根据相遇点快慢指针的路程相等列公式,推导出相遇点到环入口点和head到入口点之间的距离关系。