算法通关村第一关——链表中环的问题

1.单向链表中环的问题

力扣141,给定一个链表,判断单向链表中有无环,进一步确定环的入口。
在这里插入图片描述

1.1 第一种方法——HashMap

第一种方法是利用HashMap来解决,遍历链表时将元素加入map中,要是有环存在一定会发生碰撞,发生碰撞的位置也就是环的入口位置。

var detectCycle = function(head) {
	let pos = head;
	const nodeMap= new Map();
	while (pos) {
		if (nodeMap.has(pos)) return pos;
		else {
            nodeMap.set(pos, true);  // 存的是结点的地址引用而非节点值
			pos = pos.next;
        }
	}
	return null;
}

1.2 第二种方法——快慢指针法

确定是否有环存在最有效的方法就是快慢指针,那么快慢指针为什么一定会相遇呢?

这就像两个人在操场跑圈一样,一个人跑的快另外一个人跑得慢,不管这两个人的跑步速度是多少,跑得快的那个一定会遇见跑得慢的那个人!

如图一个环形链表,快慢指针相遇在y点后,快指针回到起点,快慢指针以相同速度推进,会在环的入口x位置相遇。

在这里插入图片描述

①假如跑一圈就相遇

让快指针走两步,慢指针走一步,那么第一次相遇时,快指针走了a+b+c+b步,慢指针走了a+b步,根据等式关系 2 ( a + b ) = a + b + c + b 2(a+b)=a+b+c+b 2(a+b)=a+b+c+b可得 a = c a=c a=c,此时让快指针回到起点,快慢指针同时走,相遇的位置就是环的入口。

②假如跑n圈才相遇

相遇时快指针走了a+n(b+c)+b步,慢指针走了a+b步,根据等式关系 2 ( a + b ) = a + n ( b + c ) + b 2(a+b) = a+n(b+c)+b 2(a+b)=a+n(b+c)+b 可得 a = c + ( n − 1 ) ( b + c ) a=c+(n-1)(b+c) a=c+(n1)(b+c) a = c + ( n − 1 ) l e n g t h O f C i r l c e a=c+(n-1)lengthOfCirlce a=c+(n1)lengthOfCirlce ,这说明当快慢指针相遇时,快指针已经在环里溜达很多圈儿了。

找到环形链表的入口可以这样做:

// 双指针方法找到环形链表的入口
var findEnterOfCircle = function(head) {
	if (!head || !head.next) return null;

	let fastPointer = slowPointer = head;
	while (fastPointer && fastPointer.next) {
		fastPointer = fastPointer.next.next;
		slowPointer = slowPointer.next;
		// 如果快慢指针相遇,那么让快指针回到起点,两个指针同时向前走,再相遇时就是环形链表入口位置
		if (fastPointer === slowPointer) {
			fastPointer = head;
			while (fastPointer !== slowPointer) {
				fastPointer = fastPointer.next;
				slowPointer = slowPointer.next;
			}
			return fastPointer;
		}
	}
	return null;
}

第二种确认入口的方法如下,也是双指针,只不过用了三次。

// 三次双指针方法
/*思想:
第一次确定是否存在环,
第二次找到环的长度,让快指针固定在相遇位置,慢指针继续向前走,再相遇时就视为一次遍历得到环的长度
第三次通过找倒数第K个结点的方法来找入口,先让快指针走K步,再让两个指针同时走,当快指针来到相遇点时,慢指针所指位置就是入口
*/

var findEnterOfCircle = function(head) {
	// 第一次双指针
	if (!head || !head.next ) return null;

	let fastPointer = slowPointer = head;
	let lengthOfCircle = 0;
	while (fastPointer && fastPointer.next) {
		fastPointer = fastPointer.next.next;
		slowPointer = slowPointer.next;
		// 如果两个指针相遇了,说明有环
		if (fastPointer === slowPointer) {
			// 开始第二次双指针,得到环的长度
			slowPointer = slowPointer.next;
			while (slowPointer !== fastPointer) {
				++lengthOfCircle;
				slowPointer = slowPointer.next;
			}
		}
		// 开始第三次双指针
		for (let i = 1; i > 0 ; i--) {
			fastPointer = fastPointer.next;
		}
		while (fastPointer !== slowPointer) {
			fastPointer = fastPointer.next;
		    slowPointer = slowPointer.next;
		}
		return slowPointer;
	}
	return null;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值