2021.5.18
作者:互联网
面试题 02.08. 环路检测 (Linked List Cycle LCCI)
给定一个链表,如果它是有环链表,实现一个算法返回环路的开头节点。
如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。
思路与题解:
1.通过哈希表解决该问题:将每个节点丢进java的HashSet中,如果有重复的节点那就返回该节点
题解:
public ListNode detectCycle(ListNode head) { ListNode p = head; Set<ListNode> list = new HashSet<ListNode>(); while(p != null){ if(list.contains(p)){ return p; }else{ list.add(p); } p = p.next; } return null; }
2.利用快慢指针,同时利用floyd判环算法:
首先先用两个指针,慢指针每次走一步,快指针每次走两步(走两步与走多步的效果是等价的,不过步数的增加可能导致算法的复杂度增加),若快慢指针在某处相遇则说明该链表有环,若不相遇则说明没有环。当快慢指针相遇后,再用一个新的指针指向链表头节点,新指针与慢指针同时移动一个位置,当他们相遇时,新指针所指位置即为环路开始节点。(如果有不理解的可以划至下方要点分析)
题解:
public ListNode detectCycle(ListNode head) { if(head == null){ return null; } ListNode slow = head; ListNode flast = head; while(flast != null){ slow = slow.next; if(flast.next != null){ flast = flast.next.next; }else{ return null; } if(flast == slow){ ListNode ptr = head; while(ptr != slow){ ptr = ptr.next; slow = slow.next; } return ptr; } } return null; }
要点分析:
一、HashSet:
java中的HashSet是Set接口的典型实现,大多数时候使用Set集合时就是使用这个实现类。
HashSet按Hash算法来存储集合中的元素,因此具有很好的存取和查找性能。其底层数据结构是哈希表。
HashSet具有以下特点:
1、不能保证元素的排列顺序,顺序可能与添加顺序不同,顺序也可能发生变化;
2、HashSet不是同步的;
3、集合元素值可以是null;
HashSet内部地存储机制:
存入元素时,HashSet会调用该元素对象的hashCode方法来获取该元素对象的hashCode值从而根据该hashCode值去分配该元素对象再HashSet中的存储位置。
也就意味着会出现以下情况:
即使两个对象的值相同,而hashCode值不同,那么HashSet就会为这两个元素分配不同的存储位置,从而出现了,HashSet中的元素对象不唯一。
出现这种情况时,我们可以通过重写对象的hashCode与equal方法,使得HashSet中存储元素对象唯一。
二、Floyd判环算法:
假设有一链表中具有环路,那么我们用步长为一的慢指针与步长为二的快指针自该链表的头节点开始进行遍历,最终在环路内,快慢指针将会相遇:
我们假设慢指针在环路内过长度为n的距离后与快指针相遇,那么此时慢指针所走过的路程为:
m+A*(n+k)+n (A为慢指针走过A圈,假设该环路足够大);
快指针所走过路程为:
m+B*(n+k)+n。
因为快指针路程总是慢指针的两倍,那么我们设慢指针所走过路程为S,可得
S=2S-S=(B-A)*(n+k)
此时我们再用一个新的指针从链表头节点出发,慢指针继续行走,那么当新指针到达环路入口时,
S(新)=m
S(慢)=m+(B-A)*(n+k)
因为(n+k)为环路长度,所以此时,新指针与慢指针在环路入口相遇,我们也就找到了环路入口.
标签:环路,2021.5,HashSet,18,next,链表,null,指针 来源: https://www.cnblogs.com/happybrother/p/14779731.html