floyd判圈算法

经典的三个问题:

1.如何判断是否有环?如果有两个头结点指针,一个走的快,一个走的慢,那么若干步以后,快的指针总会超过慢的指针一圈。
2.如何计算环的长度第一次相遇(超一圈)时开始计数第二次相遇停止计数。
3.如何判断环的入口点:碰撞点p到连接点的距离=头指针到连接点的距离,因此,分别从碰撞点头指针开始走,相遇的那个点就是连接点来源

一、判断是否有环《采用快慢指针来解决》
最大的问题就是“在快指针遇到慢指针时,慢指针还没有走完一圈”,可以想作快指针肯定比慢指针先进入环,当慢指针进入环的时候,快指针就以每次快1步的速度追慢指针。
一个极端的例子:
环的长短为n,慢指针速度为1,快指针速度为2。当慢指针走完一圈的时候,所花的时间快指针走完了两圈,第一圈是领先到达,第二圈是相遇到达。如果快指针速度越快那么相遇得越早。
在这里插入图片描述

public class Solution {
    public boolean hasCycle(ListNode head) {
        if (head == null || head.next == null) { // 为了初始化
            return false;
        }
        ListNode slow = head;
        ListNode fast = head.next;
        while (slow != fast) { // 相遇时退出
            if (fast == null || fast.next == null) { // 快指针先到达尾部
                return false;
            }
            slow = slow.next; // 走1步
            fast = fast.next.next; // 走2步
        }
        return true;
    }
}

二、环的长短
当slow和fast相遇时必然在环内,由于slow每次移动1步,而fast每次移动2步,从此时开始计数,当两者再次相遇时,两者的差值就是一个环的长度。

int cycleLen(ListNode head)
{
	if(hasCycle(head))
		return 0;
	ListNode fast = head;
	ListNode slow = head;
	int length = 0;
	bool begin = false;
	bool agian = false;
	while( fast != null && fast.next != null) // 防止空指针异常
	{
		fast = fast.next.next;
		slow = slow.next;
		//超两圈后停止计数,挑出循环
		if(fast == slow && agian == true)
			break;
		//超一圈后开始计数
		if(fast == slow && agian == false)
		{			
			begin = true; // 开始计数的标准
			agian = true; // 结束计数的标志
		}
 
		//计数
		if(begin == true)
			++length;
		
	}
	return length;
}

三、环的入口
设相遇前的节点数为a,环的节点数为b
第一次相遇:
slow: s = s
fast: f = s + nb = 2s(由于fast每次移动2步)
s = nb就是slow在第一次相遇的时候已经走了nb
只要两者在入口处相遇就可以求得入口。
而s = a + nb就是在入口处,所以只需要slow再移动a步就可以在入口处。而此时不知道a为多少,可以令fast=head,然后slow和fast都每次移动一步,当f=a时,s=a + nb就会第二次在入口处相遇。

public class Solution {
    public ListNode detectCycle(ListNode head) {
        if (head == null || head.next == null){
            return null;
        }
        ListNode slow = head;
        ListNode fast = head;
        while(fast != null && fast.next != null){
            slow = slow.next;
            fast = fast.next.next;
            if (slow == fast){ // 找到了环--判断有环
                break; // 此时slow=fast第一次相遇
            }
        }
        // 如果是由于fast遇到null退出循环
        if (fast == null || fast.next == null){
            return null;
        }
        fast= head; // 初始化为头结点
        while(fast != slow){
            fast = fast.next; // 在转圈等待
            slow = slow.next;
        }
        return slow;

    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值