算法-003-链表是否有环及环的入口

更多好文
v
golang技术实验室

给定链表,判断链表是否有环及环的位置
如下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VP8ttPm8-1625816200798)(readme.assets/image-20210706104315663.png)]

第一种-时间判断

给懂时间1s或者0.5s,来判定是否循环完毕。

此方法会有误差,如果链表足够长,并且没有环,而超过时间就会认定是有环

第二种-set查重

设置map集合,将每次循环的链表元素地址存储在map中,当出现在map中存在的情况时候,就是存在环,而且能找出环的入口

//-- ----------------------------
//--> @Description 链表是否有环 及环的入口
//--> @Param
//--> @return
//-- ----------------------------
func isRingSet(head *nodeList)*nodeList{
	var Map = make(map[string]struct{})
	cur := head
	for cur != nil && cur.Next != nil{
		if _ ,ok := Map[fmt.Sprintf("%p",cur)];ok{
			return cur
		}
		Map[fmt.Sprintf("%p",cur)] = struct{}{}
		cur = cur.Next
	}
	return nil
}

第三种-快慢指针

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Iflttvgh-1625816200800)(readme.assets/image-20210706140852681.png)]

第一步

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hTHF3Fre-1625816200802)(readme.assets/image-20210706140910943.png)]

指定快慢指针,slow步幅为1,fast步幅为2

第1次移动

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l8RlxbdN-1625816200804)(readme.assets/image-20210706141019772.png)]

第二次移动

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OtFUSfQV-1625816200805)(readme.assets/image-20210706141040453.png)]

第三次移动

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rJ5IovIs-1625816200806)(readme.assets/image-20210706141101223.png)]

此时fast已经进入第二圈

第四次移动

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6wI8BFeo-1625816200807)(readme.assets/image-20210706141118452.png)]

此时fast和slow相遇,说明存在环

代码

//-- ----------------------------
//--> @Description 链表是否有环
//--> @Param
//--> @return
//-- ----------------------------
func isRingCycle(head *nodeList)*nodeList{
	slow , fast := head,head
	for  slow != nil  && fast != nil && fast.Next != nil{
		slow = slow.Next
		fast = fast.Next.Next
		if slow == fast{
			return slow
		}
	}
	return nil
}
slow &{2 0xc00008e200}
fast &{3 0xc00008e210}
slow &{3 0xc00008e210}
fast &{5 0xc00008e230}
{4 0xc00008e220}

环入口位置的确定

第一种

我们可以用hashset的方式,既能确定是否有环,能确定环的入口

第二种

快慢指针

快指针fast和慢指针slow从头开始出发,快指针每次走2步,慢指针每次走1步,当第一次相遇的时候,快指针从头出发,每次走一步,再次相遇的时候就是环的入口

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IGDhceZ5-1625816200807)(readme.assets/image-20210709144019404.png)]

/**
快慢指针判断环的入口

fast = 2,slow = 1 当第一次相遇时候,快指针从起点走,
慢指针正常前行,当第二次相遇的时候就是环的入口

*/
func loopCycle(head *nodeList)*nodeList{
	slow,fast := head,head
	for slow != nil && fast.Next != nil{
		slow = slow.Next
		fast = fast.Next.Next
		if slow == fast{
			break
		}
	}

	fast = head
	for  slow != nil && fast != nil && fast.Next != nil{
		fast = fast.Next
		slow = slow.Next
		if fast == slow{
			return slow
		}
	}
	return nil
}

我们来论证一下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sXnRn7vM-1625816200808)(readme.assets/image-20210709144703903.png)]

假设头节点到环入口的举例为x,环入口到第一次相遇的举例为y,第一次相遇到环入口的位置为z,
则slow走的举例为x+y,而fast走的距离为x+y +n(y+z),也就是fast在环中走了n圈(n>=0),因为
fast每次走2步,slow走一步,所以fast是slow的2倍

2(x+y) = x+y +n(y+z)
x = n(y+z)-y
x=(n-1)(y+z)+z

y+z是环的长度

有两种情况:

n=1 时,x=z,此时我们将 fast 放到链表头,然后 fast 和 slow 每次走一步,相遇节点就是环的入口;
n>1 时,我们将 fast 放到链表头,当 fast 和 slow 相遇时,说明 slow 在环里转了 n-1 圈后又走了z步,等价于 n=1 的情况。

更多好文
v
golang技术实验室

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

a...Z

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值