部分有环单向链表判断环起点问题

本文介绍了两种检测链表中环的算法:哈希法和快慢指针法。哈希法通过记录遍历过的节点来判断是否存在环。快慢指针法则利用两个速度不同的指针,当它们相遇时说明存在环,并详细解析了如何找到环的入口。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

# 方法一 (hash)

ListNode *detectCycle(ListNode *head) {
	unordered_set<ListNode*> map;
	int index = 0;
	while (head)
	{
		if (map.count(head))
			return head;
		else
			map.insert(map.begin(), head);
		index++;
		head = head->next;
	}
	return NULL;
}

# 方法二 (快慢指针)

ListNode *detectCycle(ListNode *head) {
		ListNode *slow = head, *fast = head;
		while (fast && fast->next)
		{
			slow = slow->next;
			fast = fast->next->next;
			if (slow == fast)
				break;
		}
		if (!fast || !fast->next)
			return NULL;
		slow = head;
		while (slow != fast)
		{
			slow = slow->next;
			fast = fast->next;
		}
		return fast;
	}

解释一下:

快指针每次走2,慢指针每次走1,快指针走的距离是慢指针的两倍。而快指针又比慢指针多走了一圈。所以head到环的起点+环的起点到他们相遇的点的距离 与 环一圈的距离相等。现在重新开始,head运行到环起点 和 相遇点到环起点 的距离也是相等的,相当于他们同时减掉了 环的起点到他们相遇的点的距离

推算公示也很简单:

<think>嗯,用户想知道怎么检测单向链表里的,还要找到入的第一个节点。这个问题我之前好像学过,但得仔细想想怎么一步步解决。 首先,检测的存在,常见的办法应该是快慢指针吧。快指针每次走两步,慢指针每次走一步。如果有的话,它们肯定会相遇。这点我记得是对的,因为如果没,快指针会先走到末尾,而如果有,快指针会在里绕圈,最终和慢指针碰上。 那怎么确定入节点呢?这时候可能得用数学方法推导。假设相遇时,慢指针走了k步,快指针走了2k步。外的长度是L,的长度是C,相遇点距离入点的距离是m。那么,慢指针在里走了k-L步,也就是m的位置。而快指针已经绕了n圈,所以2k = L + nC + m。然后因为快慢指针相遇时,快指针比慢指针多走了一圈?或者可能需要用L和C的关系来找。这时候可能需要把其中一个指针移回到起点,然后两个指针都每次走一步,再次相遇的点就是入点。这个步骤的依据是什么? 比如,当快慢指针相遇后,把慢指针留在原地,快指针回到头节点,然后两个指针都每次走一步,再次相遇的位置就是入点。或者是不是其中一个指针回到头节点?这时候可能需要重新计算步数。比如,当第一次相遇时,L = (n-1)C + (C - m),所以当两个指针再次以相同速度走时,会在入点相遇。 还有,空间复杂度要求O(1),所以不能用哈希表存储访问过的节点。所以必须用快慢指针法。 那具体步骤应该是这样的:先判断是否有,如果有,再找入点。在判断后,将一个指针重置到头节点,然后两个指针同步移动,直到相遇点即为入点。 比如,引用中的资料提到,快慢指针相遇后,如何找到切入点。根据引用2,快慢指针法需要一些步骤来找到入点。可能这时候需要数学推导。比如,当第一次相遇时,头节点到入点的距离是A,入点到相遇点的距离是B,的剩余部分是C。则相遇时,慢指针走了A + B,快指针走了A + B + nC。而快指针的速度是慢指针的两倍,所以2(A+B) = A + B + nC → A + B = nC → A = nC - B。这时候,如果让一个指针从头开始走A步,另一个从相遇点走,剩下的nC - B步刚好等于A,所以相遇点就是入点。 所以步骤应该是: 1. 快慢指针找到相遇点,确认有。 2. 然后,将其中一个指针(比如快指针)放回头节点,然后两个指针每次各走一步,再次相遇的点就是入点。 这样就能满足空间复杂度O(1)的要求了。 那么,具体的实现代码该怎么写呢?比如,在Python中,先初始化快慢指针,然后循直到相遇,如果有的话。然后重置一个指针到head,再同步移动直到相遇。这时候返回该节点的值即可。 需要注意边界情况,比如链表为空或者只有一个节点的情况。这时候直接返回-1,因为没有的可能。 比如,如果链表为空或只有一个节点,直接返回-1。否则,快慢指针初始化为headhead.next?或者都是head?要看具体实现。比如,初始化slow = head,fast = head.next,然后循的条件是fast和fast.next不为空。这样可能更快检测到相遇。 或者在开始的时候,先判断head是否为null或者head.next为null,这时候直接返回-1,因为没有的可能。否则,slow和fast都从head开始,每次slow走一步,fast走两步。 举个例子,假设链表结构为1->2->3->4->2(在2的位置)。这时候,快慢指针会在某个点相遇,比如节点4或者节点3?然后按照上述方法,重置一个指针到head,然后同步走,直到相遇在节点2。 这样就能正确找到入点了。 总结下来,步骤就是快慢指针检测,找到相遇点后,再通过同步移动找到入点。这应该就是正确的方法,符合空间复杂度O(1)的要求。</think>### 检测单向链表中的及定位入节点的方法 #### 一、检测是否存在 使用**快慢指针法**(Floyd判圈算法): 1. 初始化两个指针$slow$和$fast$,均指向链表头节点。 2. $slow$每次移动1步,$fast$每次移动2步。 3. 若链表,$fast$会先遇到空节点。 4. 若链表,$fast$最终会追上$slow$(两者相遇)[^3]。 #### 二、定位入节点 1. 当快慢指针相遇后,将$fast$重新指向链表头节点,保持$slow$在相遇点。 2. 两个指针改为每次均移动1步。 3. 当$fast$和$slow$再次相遇时,相遇点即为入节点[^2]。 #### 三、数学推导 假设: - 头节点到入节点的距离为$L$。 - 的周长为$C$。 - 第一次相遇时,$slow$移动了$L + m$步,$fast$移动了$L + m + nC$步($n$为整数)。 - 根据速度关系:$2(L + m) = L + m + nC \implies L = nC - m$。 - 因此,从头节点移动$L$步,与从相遇点移动$nC - m$步,最终会在入节点重合。 #### 四、代码实现(Python) ```python class ListNode: def __init__(self, val): self.val = val self.next = None def detect_cycle(head): if not head or not head.next: return -1 slow = head fast = head # 检测是否存在 has_cycle = False while fast and fast.next: slow = slow.next fast = fast.next.next if slow == fast: has_cycle = True break if not has_cycle: return -1 # 定位入节点 fast = head while fast != slow: fast = fast.next slow = slow.next return slow.val ``` #### 五、示例分析 以链表$1 \to 2 \to 3 \to 4 \to 2$(在节点2)为例: 1. 快慢指针在节点4相遇。 2. 将$fast$重置到头节点1,同步移动后: - $fast$移动路径:$1 \to 2$。 - $slow$移动路径:$4 \to 2$。 3. 最终在节点2相遇,即入节点。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值