判断链表中是否有环
题目描述
判断给定的链表中是否有环。如果有环则返回true,否则返回false。
你能给出空间复杂度 O ( 1 ) O(1) O(1)的解法么?
题解
方法一:快慢指针
慢指针,每次走一步,快指针每次走两步,如果相遇就说明有环存在。如果其中一个为空,说明没有环存在。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
快慢指针
if(head==NULL || head->next==NULL)
return false;
ListNode *slow = head;
ListNode *fast = head;
while(fast!=NULL && fast->next!=NULL) {
slow = slow->next;
fast = fast->next->next;
if(slow == fast)
return true;
}
};
方法二:逐个删除
从链表的头部开始逐个删除,(将结点的next指针指向自己)。如果没有环,从头结点逐个删除,最后会删除完成为NULL。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head==NULL || head->next==NULL)
return false;
if(head->next == head)
return true;
ListNode *nextNode = head->next;
head->next = head;
return hasCycle(nextNode);
}
};
方法三:取出节点,set存放
该题也可以将全部的结点逐个取出,完整的存放在set集合中。每次存放时,判断set集合中是否存在重复元素,如果存在,说明该链表有环。
STL模板的排序默认是使用<来排序,而set中每一个元素的值都是唯一的,而且系统能够根据元素的值进行自动排序。所以在使用set存储结构体时,就需要对特定的运算符进行重载。在本例中,需要对<进行重载,而且要对struct中的key和value都考虑到。struct ListNode { int val; ListNode *next; ListNode(int x) : val(x), next(NULL) {} bool operator <(const ListNode &L) const { if(val < L.val) return true; if(val == L.val) return next.compare(L.next) < 0; return flase; } };
但是在本题中,结构体时给定的内容,所以该方法不再适用。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head==NULL || head->next==NULL)
return false;
set<ListNode *> s;
while(head != NULL) {
if(s.count(head))
return true;
s.insert(head);
head = head->next;
}
return false;
}
};
方法四:取出结点,map存放
使用STL中的map进行映射。首先定义map<ListNode *, int> m
,将一个ListNode *
指针映射成为数组的下标,并赋值一个int
类型的数值。
从链表的头部开始逐个遍历,每遇到一个指针p
,都要判断map[p]
是否为0。如果为0,将map[p]
赋值为1,表示该结点是第一次访问;如果map[p]
的值为1,说明这个结点已经存在了,说明链表有环存在。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head==NULL || head->next==NULL)
return false;
map<ListNode *, int> m;
while(head != NULL) {
if(m[head] == 0)
m[head] = 1;
else if(m[head] == 1)
return true;
head = head->next;
}
return false;
}
};
方法五:每个结点都指向一个共同结点
从链表的头部开始遍历,将每一个结点都指向一个共同的新建结点。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head==NULL || head->next==NULL)
return false;
ListNode *ptemp = new ListNode(0);
ListNode *pre = nullptr;
while(head != NULL) {
if(head->next == ptemp)
return true;
pre = head;
head = head->next;
pre->next = ptemp;
}
return false;
}
};
方法六:反转链表
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head==NULL | head->next==NULL)
return false;
ListNode *phead = head;
ListNode *pnext = head->next;
ListNode *pnextnext = head->next->next;
while(pnextnext != NULL) {
if(pnext == head)
return true;
pnext->next = phead;
phead = pnext;
pnext = pnextnext;
pnextnext = pnextnext->next;
}
return false;
}
};