24.两两交换链表中的节点
19.删除链表的倒数第N个节点
面试题 02.07. 链表相交
142.环形链表II
代码随想录
本题思路采用虚拟头结点和移动指针
背景是交换,那肯定移动指针要在需要交换的节点前面
所以移动指针循环的条件是下一个且下一个的下一个不为空
然后每次就将下一个和下一个的下一个的位置对调,指针方向改变,这里需要用两个个临时指针来存储第一个和第三个节点的位置,(看代码更清晰)
假如是奇数指针这种方法也是可行,因为我存了第三个节点的位置,就是我在调换的时候仍能保持第三个节点在链表中的第三个位置
/**
* 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 dummy=new ListNode(-1,head);
// 初始化移动指针指向虚拟头结点
ListNode cur=dummy;
// 两个结束条件,因为cur要在移动的两个元素之前,所以分别是偶数时的cur.next!=null
// 还有奇数时的cur.next.next!=null
// 注意先后顺序,因为cur.next如果为null,那么cur.next.next会报空指针异常,所以要注意先后顺序
while(cur.next!=null&&cur.next.next!=null)
{
//建议画图
//先存储1和3位置为临时值
ListNode temp1=cur.next;
ListNode temp2=cur.next.next.next;
//移动指针所在位置指向2
cur.next=cur.next.next;
//2指向1
cur.next.next=temp1;
//1指向3
temp1.next=temp2;
//移动指针向前一次性走两格
cur=cur.next.next;
}
//返回虚拟头结点的下一位置也就是整个链表
return dummy.next;
}
}
删除,那仍然是要到被删除节点的前一位,采用虚拟头结点+双指针的方法
初始化快慢指针(或者可以说先后指针),两个指针之间间距N个位置,同时移动,直到快指针移动到空,慢指针所在的位置就是要删除元素的前一个位置,然后进行删除
/**
* 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 dummy=new ListNode(-1,head);
// 初始化快慢指针
ListNode fast=dummy;
ListNode slow=dummy;
//快指针先走n+1步,记住此时快指针不能走到边缘
//慢指针保持不变
for(int i=0;i<=n;i++){
fast=fast.next;
}
//快慢指针同时移动,直到快指针指向null
while(fast!=null){
fast=fast.next;
slow=slow.next;
}
//此时慢指针所在位置就是我我们要删除的元素的前一位,进行删除
slow.next=slow.next.next;
//返回整个列表
return dummy.next;
}
}
本题采用双指针方法
1.首先初始化两个移动指针算出两个链表的长度
2.将两个移动指针重置
3.我们默认第一个链表更长(后面代码也是),要是比较结果是第二个链表更长的话,那就将两个链表更换,首先更换长度,再更换头指针,都要用到中间值
4.再将链表一的移动指针向前移动两个链表差值的距离,可以看看实例3来理解,相当于统一起跑点
5.此时再将两个移动指针同时走直到相遇,中途没走一次比较一次,直到两者其中之一为空(同一起跑点其实也相当于两个移动指针后面能走的距离是一样的)
6.直接比较指针所在的节点,不能比较值,值相等不一定是同一节点
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
//双指针解决
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
// 初始化两个链表的移动指针分别指向两个链表的头部
ListNode curA=headA;
ListNode curB=headB;
// 得到每个链表的长度
int lenA=0;
while(curA!=null){
curA=curA.next;
lenA++;
}
int lenB=0;
while(curB!=null){
curB=curB.next;
lenB++;
}
//回归初始位置
curA=headA;
curB=headB;
//判断两个长度谁长,要是B长的话,需要A和B互换
if(lenB>lenA){
// 长度互换
int temp1=lenA;
lenA=lenB;
lenB=temp1;
//头指针互换,相当于换链表
ListNode temp2=curA;
curA=curB;
curB=temp2;
}
//长度的差值
int len=lenA-lenB;
//需要将A向前移动差值的距离,可以看看实例3来理解,相当于统一起跑点
while(len-->0){
curA=curA.next;
}
//两者不断往前走
while(curA!=null&&curB!=null){
if(curA==curB){
return curA;
}
//不一样就继续前进
curA=curA.next;
curB=curB.next;
}
return null;
}
}
本题采用快慢指针的思路
简单思路(经典题):快指针每次走两步,慢指针每次走一步,那么两个指针肯定会遇到,遇到后找到交点,根据数学公式,在交点处放一个指针,和头指针同时走,遇到的点就是环的入口,再返回这个点作为结果
数学公式(不作过多讲解,贴图)
图片来源:代码随想录
/**
* 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){
//当前位置指针与头指针同时走相遇的点就是环的头
while(fast!=head){
fast=fast.next;
head=head.next;
}
return head;
}
}
return null;
}
}