思路:快慢指针实现
用两个指针,一个指针一次走一步,另一个指针一次走两步,如果存在环,则这两个指针会在环内相遇,时间复杂度为O(n)
/** * 检测单链表中是否有环 */ public static boolean hasCircle(ListNode head) { if(null == head) { return false; } ListNode slow = head; ListNode fast = head; while(fast != null && fast.next != null){ fast = fast.next.next; slow = slow.next; if(fast == slow){ return true;//两个指针相遇 } } return false; }
拓展1:如果单链表有环,找出环的入口节点(环的连接点)
/** *如果单链表有环,找出环的入口节点(环的连接点)。 *这里先证明一个定理:碰撞点到连接点的距离=头指针到连接点的距离 *假设单链表的总长度为L,头结点到环入口的距离为a,环入口到快慢指针相遇的结点距离为x,环的长度为r, *慢指针总共走了s步,则快指针走了2s步。另外,快指针要追上慢指针的话快指针至少要在环里面转了一圈多(假设转了n圈加x的距离) *得到以下关系: *s = a + x *2s = a + nr + x *=>a + x = nr *=>a = nr - x //入口点 */ public static ListNode searchEntranceNode(ListNode head) { ListNode slow=head;//p表示从头结点开始每次往后走一步的指针 ListNode fast=head;//q表示从头结点开始每次往后走两步的指针 while(fast !=null && fast.next !=null) { slow=slow.next; fast=fast.next.next; if(slow==fast) { break;//p与q相等,单链表有环 } } if(fast == null || fast.next == null){ return null; } slow=head; while(slow != fast) { slow=slow.next; fast=fast.next; } return slow; }
public static ListNode searchEntranceNode1(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){ fast=slow;//相遇点 slow=head; while(slow != fast){ slow = slow.next; fast = fast.next; } if(slow == fast) return fast; } } return null; }
拓展二:求链表中环的长度
/** * 求环的长度 */ public static int countCircleSize(ListNode head) { ListNode entranceNode = searchEntranceNode1(head);//环的入口点 if(entranceNode == null) { return 0; } ListNode slow = entranceNode.next; int size = 1; while(slow != entranceNode) { slow = slow.next; size++; } return size; }