给你一个链表,两两交换其中相邻的节点,并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题(即,只能进行节点交换)。
解题思路:通过双指针first 和second,每一轮都赋两个新值first=curr, second=curr.next
/**
* 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) {
if(head==null || head.next==null){
return head;
}
ListNode dummy=new ListNode(0);
dummy.next=head;
ListNode prev=dummy, curr=head;
while(curr!=null && curr.next!=null){
ListNode first=curr;
ListNode second=curr.next;
//交换相邻节点位置
first.next=second.next;
second.next=first;
prev.next=second;
//更新prev和curr以进行下一轮迭代
prev=first;
curr=first.next;
}
return dummy.next;
}
}
给你一个链表,删除链表的倒数第 n
个结点,并且返回链表的头结点。
解题思路:我们首先将第一个指针移动n+1个节点,然后我们移动两个指针,直到第一个指针到达链表的末尾,最后切记return dummy.next而不是return head。
/**
* 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 {
//双指针法:一个指针先移动n步,然后两个指针同时移动,当先行的指针到达链表末尾时,另一个指针就位于倒数第n个节点
public ListNode removeNthFromEnd(ListNode head, int n) {
if(head==null || n<=0){
return head;
}
ListNode dummy=new ListNode(0);
dummy.next=head;
ListNode fast=dummy,slow=dummy;
for(int i=0;i<n;i++){
fast=fast.next;
}
while(fast.next!=null){
fast=fast.next;
slow=slow.next;
}
slow.next=slow.next.next;
return dummy.next;
}
}
给你两个单链表的头节点 headA
和 headB
,请你找出并返回两个单链表相交的起始节点。如果两个链表没有交点,返回 null
。
同leetcode题:160.链表相交
解题思路:使用一个简单的循环同时移动pA,pB 。当两个指针相遇时,他们会指向相交节点的位置,如果两个链表没有交点,那么当一个链表遍历完后,另一个链表的指针会从头节点开始遍历
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
public class Solution {
//双指针法:同时移动pA和pB,当它们相等时,会指向相交节点的位置
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if(headA==null || headB==null){
return null;
}
ListNode pA=headA,pB=headB;
while(pA!=pB){
pA=pA==null?headA:pA.next;
pB=pB==null?headB:pB.next;
}
return pA;
}
}
给定一个链表的头节点 head
,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
。
如果链表中有某个节点,可以通过连续跟踪 next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数 pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果 pos
是 -1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。
不允许修改 链表。
解题思路:
此时已经可以判断链表是否有环了,那么接下来要找这个环的入口了。
假设从头结点到环形入口节点 的节点数为x。 环形入口节点到 fast指针与slow指针相遇节点 节点数为y。 从相遇节点 再到环形入口节点节点数为 z。 如图所示:
那么相遇时: slow指针走过的节点数为: x + y
, fast指针走过的节点数:x + y + n (y + z)
,n为fast指针在环内走了n圈才遇到slow指针, (y+z)为 一圈内节点的个数A。
因为fast指针是一步走两个节点,slow指针一步走一个节点, 所以 fast指针走过的节点数 = slow指针走过的节点数 * 2:
(x + y) * 2 = x + y + n (y + z)
两边消掉一个(x+y): x + y = n (y + z)
因为要找环形的入口,那么要求的是x,因为x表示 头结点到 环形入口节点的的距离。
所以要求x ,将x单独放在左面:x = n (y + z) - y
,
再从n(y+z)中提出一个 (y+z)来,整理公式之后为如下公式:x = (n - 1) (y + z) + z
注意这里n一定是大于等于1的,因为 fast指针至少要多走一圈才能相遇slow指针。
这个公式说明什么呢?
先拿n为1的情况来举例,意味着fast指针在环形里转了一圈之后,就遇到了 slow指针了。
当 n为1的时候,公式就化解为 x = z
,
这就意味着,从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。
也就是在相遇节点处,定义一个指针index1,在头结点处定一个指针index2。
让index1和index2同时移动,每次移动一个节点, 那么他们相遇的地方就是 环形入口的节点。
那么 n如果大于1是什么情况呢,就是fast指针在环形转n圈之后才遇到 slow指针。
其实这种情况和n为1的时候 效果是一样的,一样可以通过这个方法找到 环形的入口节点,只不过,index1 指针在环里 多转了(n-1)圈,然后再遇到index2,相遇点依然是环形的入口节点。
/**
* 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) {
if(head==null || head.next==null){
return null;
}
ListNode slow=head,fast=head;
ListNode cycleStart=null;
//寻找环的起始节点
while(fast!=null && fast.next !=null){
slow=slow.next;
fast=fast.next.next;
if(fast==slow){
cycleStart=slow;
break;
}
}
//如果快指针为空,说明链表中没有环
if(fast==null ||fast.next==null) return null;
//将快指针重新定位到链表的头节点
fast=head;
//共同移动快慢指针,直到它们再次相遇
while(fast!=cycleStart){
fast=fast.next;
cycleStart=cycleStart.next;
}
return cycleStart;
}
}