给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,输出null。
解题思路:
对于这个题,我们可以选择遍历一遍整个链表,每次用一个list存起来,然后每存一个节点的时候都看一下list里面是否存在这个节点,如果存在,就为环。且因为我们是从头开始遍历,所以第一个后来遍历到并且存在于list里面的节点肯定是环的入口。
但是考虑到可以用set,因为set中是不能存重复的元素的,所以我们可以存在set里面,每次存入一个节点的时候都只是比较加入一个节点之后和之前set的size,如果size没变,就确定刚刚这个set里面存了这个元素,就说明有环,并且第一个找到在set里面可以存在的元素就是环的入口。这样的好处是避免了每次contain都是全局扫描,时间复杂度O(N),比较size的时间复杂度是O(1)
代码:
public ListNode EntryNodeOfLoop(ListNode pHead) { if(pHead==null||pHead.next==null) return null; //因为set不能重复存储,每次只要比较set的大小,可以避免list的contains全局搜索 Set<ListNode>set=new HashSet<>(); ListNode cur=pHead; while (cur!=null){ //先记录当前set的size,方便与后面的作比较 int size=set.size(); set.add(cur); //如果这边size没有变,说明没有添加进去,就说明是有环了, // 并且我们是从头开始,碰到的第一个加不进去的肯定就是环的入口 if(size==set.size()){ return cur; } cur=cur.next; } return cur; }
补充:如果仅仅只是判断一个链表有没有环,不需要找到环的入口,考虑到空间复杂度,通常不会用一个list或者set,而是用快慢两个指针,一个一次性走一步,一个一次性走两步,如果两个指针最终相遇,就说明链表是有环的。
public boolean NodeOfLoop(ListNode pHead) { if(pHead==null||pHead.next==null) return false; ListNode quickNode=pHead; ListNode slowNode=pHead; //因为除了第一个,而我们在前面判断过不为空,快指针比满指针走的快, // 所以慢指针走的都是快指针走过的,所以每次只需要判断快指针就可以 while (quickNode!=null&&quickNode.next!=null){ //quickNode.next==pHead、quickNode.next.next==pHead和slowNode.next==pHead是存在跑了一圈回到起点的情况, //可以提前结束遍历 if(quickNode.next==pHead||quickNode.next.next==slowNode.next||quickNode.next.next==pHead||slowNode.next==pHead){ return true; } quickNode=quickNode.next.next; slowNode=slowNode.next; } return false; }