判断链表是否有环

出现环的链表:

 

主要两种方法:

1.标记法:遍历链表,没遍历一个节点就标记一下,如果遍历到下一节点已经被标记过了就说明出现环了

代码:这里巧妙用到了HashSet,很好用

 public static boolean judgmentRing(ListNode list){
        Set<ListNode> set = new HashSet<>();
        ListNode head = list;
        while (head != null){
            if(set.contains(head)){
                return true;
            }else {
                set.add(head);
                head=head.next;
            }
        }
        return false;
    }

2.快慢指针法:设两个指针(让快指针先走),快指针每次移动两个,慢指针每次移动一个,如果有环的话最终两个指针必然会有一刻重合

代码:

public static boolean judgmentRing(ListNode list){
        ListNode fast = list;
        ListNode slow = list;
        while (fast !=null && fast.next!=null){
            fast = fast.next.next;
            slow=slow.next;
            if(fast == slow){
                return true;
            }
        }
        return false;
    }

那么问题来了,如果链表有环,怎么返回成环位置的节点呢?

我们可以在以上两段代码的基础上进行修改即可

1.标记法:

public static ListNode judgmentRing(ListNode list){
        Set<ListNode> set = new HashSet<>();
        ListNode head = list;
        while (head != null){
            if(set.contains(head)){
                return head;
            }else {
                set.add(head);
                head=head.next;
            }
        }
        return null;
    }

这个比较简单,快慢指针法有些难度

2.快慢指针法,因为最后两个指针相遇只能说明有环,但是位置并不确定,我们怎么能找到成环的节点呢?

这里用到了一个简单的数学推理,接用一下leetcode官方的图片:

最终两个指针在紫色点处相遇,头节点到成环节点距离为a,成环节点和相遇点将环分成了两半,距离分别为b和c

任意时刻,fast 指针走过的距离都为slow 指针的 22 倍。因此,我们有

a+(n+1)b+nc=2(a+b) ==> a=c+(n-1)(b+c)

我们得到一个等量关系发现(n-1)(b+c)就是n个环圈吗

所以当相遇的时候在用一个指针从头节点开始每次移动一个,同时slow指针每次移动一个,当他们相遇(也就是满足上式的=),新指针就一定走了a的距离也就是在成环的节点处

这样就好做了

public ListNode judgmentRing(ListNode head) {
        ListNode fast = head;
        ListNode slow = head;
        while (fast !=null && fast.next!=null){
            fast = fast.next.next;
            slow=slow.next;
            if(fast == slow){
                ListNode ptr = head;
                while (ptr != slow) {
                    ptr = ptr.next;
                    slow = slow.next;
                }
                return ptr;
            }
        }
        return null;
    }


 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值