方法一:哈希表
用哈希表来存储所有已经访问过的节点。每次我们到达一个节点,如果该节点已经存在于哈希表中,则说明该链表是环形链表,否则就将该节点加入哈希表中。重复这一过程,直到我们遍历完整个链表即可
struct ListNode
{
int val;
ListNode* next;
ListNode(int x) : val(x), next(NULL) {}
};
class Solution
{
public:
bool hasCycle(ListNode* head)
{
if (head == nullptr) return false;
unordered_map<ListNode*,int> map;
while (head != nullptr)
{
if (map.count(head))
{
return true;
}
map.insert({ head,1 });
head = head->next;
}
return false;
}
};
int main()
{
Solution A;
ListNode* head = new ListNode(0);
ListNode* p = head;
for (int i = 1; i < 4; ++i)
{
p->next = new ListNode(i);
p = p->next;
}
p->next = head;
cout << A.hasCycle(head) << endl;
return 0;
}
时间复杂度:O(N),其中 N 是链表中的节点数。最坏情况下我们需要遍历每个节点一次
空间复杂度:O(N),其中 N 是链表中的节点数。主要为哈希表的开销,最坏情况下我们需要将每个节点插入到哈希表中一次
方法二:快慢指针
struct ListNode
{
int val;
ListNode* next;
ListNode(int x) : val(x), next(NULL) {}
};
class Solution
{
public:
bool hasCycle(ListNode* head)
{
if (head == nullptr || head->next == nullptr)
{
return false;
}
ListNode* slow = head;
ListNode* fast = head->next;
while (slow != fast)
{
if (fast == nullptr || fast->next == nullptr)
{
return false;
}
slow = slow->next;
fast = fast->next->next;
}
return true;
}
};
int main()
{
Solution A;
ListNode* head = new ListNode(0);
ListNode* p = head;
for (int i = 1; i < 4; ++i)
{
p->next = new ListNode(i);
p = p->next;
}
p->next = head;
cout << A.hasCycle(head) << endl;
return 0;
}
时间复杂度:O(N),其中 N 是链表中的节点数
当链表中不存在环时,快指针将先于慢指针到达链表尾部,链表中每个节点至多被访问两次
当链表中存在环时,每一轮移动后,快慢指针的距离将减小一。而初始距离为环的长度,因此至多移动 N 轮
空间复杂度:O(1)我们只使用了两个指针的额外空间