1 环形链表
定义两个指针,一快一慢。慢指针每次只移动一步,而快指针每次移动两步。初始时,慢指针和快指针都在位置 head,这样一来,如果在移动的过程中,快指针反过来追上慢指针,就说明该链表为环形链表。
使用 do-while 循环,可以把快慢指针的初始值都置为 head。
cpp
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步)。
cpp
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即可。
cpp
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;
}
};