环形链表
1.环形链表
- 环形链表是链表的一种特殊形式,其中链表的某个节点指向链表中已经访问过的另一个节点,形成一个闭环。在普通的单链表或双链表中,链表的末尾节点指向null,表示链表的结束。但在环形链表中,末尾节点(或链表中的任何节点)指向链表中的某个先前节点,从而形成环。
2. 环形链表 I (判断链表是否存在环)
- 链表作为一种常见的数据结构,由一系列的结点组成,它的节点包含数据和指向下一个节点的指针两部分,但在某些情况下,指向下一个结点的指针会指向前面已经访问过的结点,构成一个环。
- 快慢指针是一种有效的解决方法(也称为Floyd的循环查找算法或龟兔赛跑算法)。
2.1 快慢指针的原理
原理:快慢指针包括两个指针,一个快指针和一个慢指针,快指针每次移动两个结点,慢指针每次移动一个节点,如果链表中存在环,那么快指针和慢指针就会相遇于某个节点;如果不存在环,那么快指针就会先到达链表的尾。
2.2 检测链表存环
- 首先定义两个指针,一个快指针,一个慢指针,并初始化指向链表的头节点
struct ListNode* slow,*fast;
slow = fast = head;
- 然后定义一个循环,让快指针每次移动两个节点,慢指针每次移动一个节点
while(){
slow = slow->next;
fast = fast->next->next;
}
3.如果快指针和慢指针相遇,则说明存在环,返回true
while(){
slow = slow->next;
fast = fast->next->next;
if(slow == fast){
return true;
}
}
- 确定
while
循环的结束条件
如果链表中存在环,那么快指针和慢指针就会相遇于某个节点;如果不存在环,那么快指针就会先到达链表的尾。
当链表不存在环且结点个数为奇数时,快指针走到尾结点,不能再往后移动;
当链表不存在环且结点个数为偶数时,快指针走到尾结点的下一个位置,遍历完链表,结束循环;
所以结束的条件为fast && fast->next
while(fast && fast->next){
slow = slow->next;//慢指针每次移动一个结点
fast = fast->next->next;//块指针每次移动两个结点
if(slow == fast){//快慢指针相遇说明存在环
return true;
}
}
- 力扣相关题目:
力扣题目完整代码:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
bool hasCycle(struct ListNode *head) {
struct ListNode* slow,*fast;
slow = fast = head;
while(fast && fast->next){
slow = slow->next;
fast = fast->next->next;
if(slow == fast){
return true;
}
}
return false;
}
3.环形链表 II (返回入环处的结点)
- 当确定了链表存在环,那么下一步就是找到环的入口,即进入环的第一个节点,为了找到环的入口,同样的让快慢指针在相同速度下,快指针一次走两步,慢指针一次走一步,最终快慢指针会相遇,记录相遇点并开始继续向下走,同时让指针从链表起始位置开始遍历,两个指针最终会在环的入口处相遇。
- 首先返回环形链表入环结点的前提是有环,所以首先要确定是否有环
- 同样的初始化快慢指针,并指向头结点。
struct ListNode* slow = head,*fast = head;
- 然后定义一个循环,让快指针每次移动两个节点,慢指针每次移动一个节点
while(){
slow = slow->next;
fast = fast->next->next;
}
- 确定
while
循环的结束条件
如果链表中存在环,那么快指针和慢指针就会相遇于某个节点;如果不存在环,那么快指针就会先到达链表的尾。
当链表不存在环且结点个数为奇数时,快指针走到尾结点,不能再往后移动;
当链表不存在环且结点个数为偶数时,快指针走到尾结点的下一个位置,遍历完链表,结束循环;
所以结束的条件为fast && fast->next
while(fast && fast->next){
slow = slow->next;//慢指针每次移动一个结点
fast = fast->next->next;//块指针每次移动两个结点
}
}
4.如果快慢指针相等,说明存在环,记录它们相遇的结点为meet
,然后让头节点从头开始移动,让meet
(即快慢指针相遇的结点)向下一个节点继续移动,因为链表此时已确定有环,那么meet
和head
一定会相遇,此时它们相遇的结点就是环的入口
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
if(slow == fast)
{
struct ListNode* meet = slow;
while(head != meet)
{
head = head->next;
meet = meet->next;
}
return meet;
}
}
- 力扣相关题目:
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
typedef struct ListNode ListNode;
struct ListNode *detectCycle(struct ListNode *head) {
ListNode* slow = head,*fast = head;
while(fast && fast->next)
{
slow = slow->next;
fast = fast->next->next;
if(slow == fast)
{
ListNode* meet = slow;//记录快慢指针相遇的位置
//让头结点和快慢指针相遇结点同时移动,它们相遇处即为入口处
while(head != meet)
{
head = head->next;
meet = meet->next;
}
return meet;
}
}
return NULL;
}