例题描述

给定一个链表,返回链表开始入环的第一个结点。 如果链表无环,则返回 NULL

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

【说明:不允许修改给定的链表】


示例 1:
【图文解析】返回链表开始入环的第一个结点

  • 输入:head = [3,2,0,-4], pos = 1
  • 输出:tail connects to node index 1
  • 解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:
【图文解析】返回链表开始入环的第一个结点

  • 输入:head = [1,2], pos = 0
  • 输出:tail connects to node index 0
  • 解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:
【图文解析】返回链表开始入环的第一个结点

  • 输入:head = [1], pos = -1
  • 输出:no cycle
  • 解释:链表中没有环。

解题方法

使用快慢指针法,找到相遇点。另外创建2指针,一指针从入口出发,一指针从相遇点出发,两指针相遇点就是入口点。

这其实是一个数学问题:
假设此带环链表抽象成为一个线性结构。题设如下:

  • l从头结点到相遇点的距离
  • c从入环点到相遇点的距离
  • n为某个常数,仅代表圈数。(n >= 1)
    【图文解析】返回链表开始入环的第一个结点
  1. 快指针直到相遇共走过的路程为:l+c+(n*r)。(n代表圈数,大小由步距决定)
    慢指针直到相遇共走过的路程为:l+c

  2. 此时它们相遇了,说明走过的距离相等,即l+c+(n*r) = l+c,化简后为l=(n-1)*r+(r-c).

  3. (n-1)*r项可以忽略,因为在圈内转的圈数没有实际意义,重点还是在于相遇点。所以当一指针从相遇点开始,一指针再次从首结点开始,共同步进,步距相同时,一定会在入环点相遇,因为环外点要走r-c个单位才可以到达入环点,环内点不可能会路过入环点再走一圈,所以环内点走的距离也是r-c,二者刚好在入环点相遇。
    【图文解析】返回链表开始入环的第一个结点

代码实现一

 ListNode *detectCycle(ListNode *head) {
	if(!head){
		return NULL;
	}
	ListNode *p,*q;
	p = q = head;
	int flag = 0,c = 0;
	while(p && p->next){
		p = p->next->next;
		q = q->next;
		c++;
		if(p == q){
			flag = 1;
			break;
		}
	}
	if(!flag){
		return NULL;
	}
	p = q = head;
	while(c--){
		while(p != q){
			p = p->next;
			q = q->next;
		}
	}
	return p; 
 }

代码实现二

 ListNode *detectCycle2(ListNode *head) {
 	if (head == NULL) {
		return NULL;
	}
	struct ListNode *fast = head;
	struct ListNode *slow = head;

	while (1) {
		fast = fast->next;
		if (fast == NULL) {
			return NULL;
		}
		fast = fast->next;
		if (fast == NULL) {
			return NULL;
		}
		slow = slow->next;
		if (fast == slow) {
			break;
		}
	}

	struct ListNode *n1 = head;
	struct ListNode *n2 = slow;

	while (n1 != n2) {
		n1 = n1->next;
		n2 = n2->next;
	}
	return n1;
 }

相关文章:

  • 2021-10-17
  • 2021-12-11
  • 2021-09-30
  • 2021-12-08
  • 2021-09-11
猜你喜欢
  • 2021-07-13
  • 2022-12-23
  • 2022-01-07
  • 2021-11-18
  • 2022-02-28
  • 2021-11-26
  • 2021-12-27
相关资源
相似解决方案