给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/linked-list-cycle
解法I:遍历链表的时候,把当前节点的next指针,从节点开始到当前节点再次遍历一遍,看是否有重复,有则说明有环,复杂度为O(n²)。代码如下:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
/*
*执行用时 :268 ms, 在所有 C 提交中击败了5.26%的用户
*内存消耗 :8.6 MB, 在所有 C 提交中击败了5.60%的用户
*/
bool hasCycle(struct ListNode *head) {
if(head == NULL || head->next == NULL) return false;
struct ListNode *temp, *top=head;
for(int i = 0; head; i++) {
temp = top;
for(int j = 0; j < i; j++) {
if(head->next == temp) return true;
temp = temp->next;
}
head = head->next;
}
return false;
}
从执行时间上来看,这种解法效率是很低的。
解法II:使用快慢指针的思路。遍历链表时,慢指针向前前进一步,快指针向前前进两步。如果链表有环,则快慢指针一定会在某处相遇。若快指针最终为NULL,则链表没有环。复杂度为O(n)。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
/*
*执行用时 :12 ms在所有 C 提交中击败了65.25%的用户
*内存消耗 :8.7 MB, 在所有 C 提交中击败了5.60%的用户
*/
bool hasCycle(struct ListNode *head) {
if(head == NULL || head->next == NULL) return false;
struct ListNode *slow = head->next, *fast = head->next->next;
while(fast) {
if(fast == slow) return true;
if(fast->next == NULL) {
return false;
}
fast = fast->next->next;
slow = slow->next;
}
return false;
}
注:对于如果链表有环,快慢指针一定在某处相遇,可以这么理解:在环内,每次移动快指针比慢指针距离多1,当快指针比满指针距离恰好等于环的长度时,这两者就必然相遇。
解法III:我们合理推断节点里的值不可能为某个值(如-99999),遍历过程中判断值是否为-99999,如果不是则将其赋值为-99999,如果是则表明有环。这是一种很“投机取巧”的解法,把链表的结构给破坏了,因此不具有多大实际意义。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
bool hasCycle(struct ListNode *head) {
if(head == NULL || head->next == NULL) return false;
while(head) {
if(head->val != -999) {
head->val = -999;
head = head->next;
}
else return true;
}
return false;
}