链表判断是否有环,如有,找入环节点
一条链表如何判断是否有环?若是有环那怎么找到链表环的入口?
解决思路
- 先判断是否有环
思路: 用快慢两个指针分别从链表头开始,慢指针一次走一个节点,快指针一次走两个节点next -> next,这样如果有环那快指针务必会跑到慢指针后面,随即两者之间的距离一次会缩小一步,最终相遇。若是未相遇且快指针的 next 为 null,则说明链表无环。
- 若是有环怎么找到环入口
如果快慢指针相遇,说明有环,假设起点到入环节点的距离为L,入环节点到相遇节点的距离为x,环的周长为H,根据速度可推出下面关系见
假设n=1,也就是当快指针再走第二圈的时候和慢指针相遇,他们的关系为L=H - x
也就是说,如果这个时候有一个指针temp从起点开始走,慢指针low 从相遇的地方开始走,当temp指针与low相遇的位置就是入环节点
struct ListNode { int val; ListNode *next; ListNode(int x) : val(x), next(nullptr) {} }; // 快慢指针 class Solution { public: bool hasCycle(ListNode *head) { //空链表或只有一个节点的链表不算环 if (head == NULL || head->next == NULL) return false; ListNode *fast = head->next; ListNode *slow = head; while (fast != slow) { if (fast->next == NULL || fast->next->next == NULL) return false; fast = fast->next->next; slow = slow->next; } return true; } }; //哈希表 class Solution { public: bool hasCycle(ListNode *head) { unordered_map<ListNode*, int>mp; while (head != NULL) { mp[head]++; if (mp[head] > 1) return true; head = head->next; } return false; } };
判断是否有环+寻找入环节点
参考:https://juejin.cn/post/7188902509996408893
class Solution { public: ListNode *detectCycle(ListNode *head) { ListNode *slow = head, *fast = head, *p = head; while(fast && fast->next) { slow = slow->next; fast = fast->next->next; if(slow == fast) //如果链表存在环 { while(p != slow) { p = p->next; slow = slow->next; } return p; } } return NULL; } };
go实现
/** * Definition for singly-linked list. * type ListNode struct { * Val int * Next *ListNode * } */ func detectCycle(head *ListNode) *ListNode { fast:=head slow:=head for fast!=nil&&fast.Next!=nil{ fast=fast.Next.Next slow=slow.Next if slow==fast{ //没有环就不会相遇,这里找到快慢指针相遇的位置 for head!=slow{ //L=H-x ,L是头结点到入环节点的距离,H是环的周长,x是入环节点到相遇节点的距离 head=head.Next slow=slow.Next } return head } } return nil }
等风起的那一天,我已准备好一切