1 环形链表
定义两个指针,一快一慢。慢指针每次只移动一步,而快指针每次移动两步。初始时,慢指针和快指针都在位置 head
,这样一来,如果在移动的过程中,快指针反过来追上慢指针,就说明该链表为环形链表。
使用 do-while
循环,可以把快慢指针的初始值都置为 head
。
class Solution {
public:
bool hasCycle(ListNode *head) {
ListNode* low = head, *fast = head;
do {
if(!fast || !fast->next)
return false;
low = low->next;
fast = fast->next->next;
}while(low != fast);
return true;
}
};
2 删除链表的倒数第 N 个结点
19. 删除链表的倒数第 N 个结点 - 力扣(LeetCode)
快指针先走n
步,慢指针后走(k-n
步)。
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
// 由于可能会删除链表头部,用哨兵节点简化代码
ListNode* res = new ListNode(0, head);
ListNode* low = res, *fast = res;
while(n--) {
fast = fast->next;
}
while(fast->next) {
fast = fast->next;
low = low->next;
}
// 左指针的下一个节点就是倒数第 n 个节点
low->next = low->next->next;
return res->next;
}
};
3 环形链表II
使用快慢指针
-
fast
指针走过链表末端,说明链表无环,此时直接返回null
。 -
fast == slow
时,有环,且两指针在环中第一次相遇。将以上两式相减得到
s = nb
。而环的入口的表达式为:a + nb
,a
即从head
出发走到环入口的距离(步数),nb
即绕了几圈。又s = nb
,即慢指针再走a
步即可到达环入口。-
fast
走的步数是slow
步数的 2 倍,即f = 2s
;(解析:fast
每轮走 2 步) -
fast
比slow
多走了 n 个环的长度,即f = s + nb
;( 解析: 双指针在环内绕圈直到重合,重合时fast
比slow
多走环的长度整数倍 )。
-
-
令
fast
重新指向链表头部节点,此时f = 0
,s=nb
,令slow
和fast
同时每轮向前走 1 步。当两指针重合时,说明fast
和slow
都满足a + nb
。 即fast
指针走到f = a
,slow
指针走到s = a + nb
。 -
最后返回
slow
或fast
即可。
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* slow = head, *fast = head;
do {
if(!fast || !fast->next)
return NULL;
fast = fast->next->next;
slow = slow->next;
} while(fast != slow);
// 此时 f = 2s, f = s + nb
// 所以 s = nb
// 而环入口节点表达式:a + nb
// 所以让其在入口处汇合:
fast = head;
while(slow != fast) {
fast = fast->next;
slow = slow->next;
}
return slow;
}
};