剑指offer--链表中环的入口结点

题目描述

给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。

 

解题思路:

两种方法,HashSet结构和双指针做法。

HashSet方法:直接遍历链表,每次指向当前节点的下一个节点。如果当前节点不在set集合中,执行进集合操作。如果当前节点在set集合中,那么直接返回当前节点即为所求的环的入口节点。控制边界,如果链表为空,直接返回为null。代码如下:

/*
 public class ListNode {
    int val;
    ListNode next = null;
 
    ListNode(int val) {
        this.val = val;
    }
}
*/
import java.util.HashSet ;
public class Solution {
 
    public ListNode EntryNodeOfLoop(ListNode pHead)
    {
        if(pHead == null) return pHead ;
        HashSet<ListNode> ans = new HashSet<>() ;
        ListNode temp = pHead ;
        while(temp != null) {
            if(!ans.contains(temp)){
                ans.add(temp) ;
            } else
                return temp ;
            temp = temp.next ;
        }
        return null ;
    }
}

 

双指针做法:之前做过一道两条链表的公共节点的题目,两个链表有共同的节点,但是两个链表的长度不一定相同。假设长度为a和b。那么想一下a+b = b+a。所以我们可以在第一个链表前面添加第二个链表,在第二个链表前面添加第一个链表进行遍历,如过各自遍历到链表结束,在令其等于自身的链表头节点继续比较,此时两个链表的长度是一样的,这样通过比较就可以得到公共节点。

那么对于这道题来说,可以指定两个指针,一个步幅为1,另一个步幅为2。注意,既然有步幅为2的指针,就需要在循环条件中设定当前节点和下一节点不为null,才可以继续循环,即(head != null && head.next != null) 。因为步幅大的指针,如果没有环的情况下,肯定是在前面的,所以只对它约束就可以。

如果链表中没有环的存在,那么后者先到达链表的尾部,循环结束,在循环中添加break判断,当两个指针相遇的时候,证明链表中存在环,直接跳出循环即可。判断当前循环跳出的条件是否满足存在环的情况,如果当前节点或者下一节点为空,证明无环,return null。否则,令后者返回到链表头节点,进行后面的操作。

这里解释一下,为什么等到两个指针第一次相遇,令后者返回头节点。

如下图所示,假设相遇节点为C,那么慢的指针走了X+Y的长度,那么快的指针走了2(X+Y)的长度相遇。思考一下慢的指针走过的是ABC,后者走过的是ABCDBC。那么2(X+Y) = X + Y + CB + Y。得出CB = X。因此当两个指针相遇,慢的指针在C点处,此时我们令快的指针指回头节点,与慢的指针按照步幅为1同时向后遍历。因为AB = CB = X。所以当两个指针再次相遇的时候,即为B点,也就是所求的环的入口节点。(图为官方提供)

 

代码如下:

/*
 public class ListNode {
    int val;
    ListNode next = null;

    ListNode(int val) {
        this.val = val;
    }
}
*/
import java.util.HashSet ;
public class Solution {

    public ListNode EntryNodeOfLoop(ListNode pHead)
    {
        if(pHead == null) return pHead ;
        ListNode last = pHead,first = pHead ;
        while(first!=null && first.next!=null) {
            last = last.next ;
            first = first.next.next ;
            if(last == first) break;
        }
        if(first == null || first.next == null) return null ;
        first = pHead ;
        while(first != last) {
            first = first.next ;
            last = last.next ;
        }
        return first;
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值