判断单向链表中是否有环和查找环的入口
快慢指针
算法描述
定义两个指针slow, fast。slow指针一次走1个结点,fast指针一次走2个结点。如果链表中有环,那么慢指针一定会再某一个时刻追上快指针(slow == fast)。如果没有环,则快指针会第一个走到NULL。
实现
结点定义如下:
class Node { public Node next; public Object data; public static int sequence = 0; }
算法:
/** * 快慢指针 * @param head * @return */ public static boolean checkCircle(Node head) { Node fast = null; Node slow = null; fast = head; slow = head; while (true) { // 慢指针移动一步 if (null != slow.next) { slow = slow.next; } else { return false; } // 快指针移动两步 if (null != fast.next && null != fast.next.next) { fast = fast.next.next; } else { return false; } // 检查是否相遇 if (slow == fast) { return true; } } }
步数检查
算法描述
上面的算法只能判断链表中有没有环,如果我们想找出环的入口怎么办呢?
定义两个指针p, q。p每走一个结点(即一步),q则从头一直向后走,直到q走到NULL或p, q走到同一个结点但走过的步数不相同为止。此时q的步数就是环入口在结点中的位置。如果走到NULL则说明链表不存在环。
为什么p, q走到同一个结点但步数不相等时就说明有环呢?因为如果p, q步数相同,说明它们走过的结点是一样的,如果p, q步数不同了,则说明p是从环里走了一圈又回到了环的入口,此时q到达该结点时还没有走过环,因此步数不相等,而且此时q的步数就是环的入口。
实现
/** * 查找环的起点 * @param head * @return 返回元素的索引,从0开始。没有找到返回-1 */ public static int findCircleEntry(Node head) { Node p = head; // 总是从头开始 Node q = head; int pSteps = 0; int qSteps = 0; while (null != q.next) { q = q.next; ++qSteps; // p从头开始走 while (null != p.next) { p = p.next; ++pSteps; // 当p与q指向同一个结点时 if (p == q) { // 如果走的步数不同,则这就是入口 if (pSteps != qSteps) { return pSteps - 1; } else { // 走的步数相同,不是入口 break; } } } p = head; // 回到头结点 pSteps = 0; } // 其中有一个指针走到了头,说明没有环 return -1; }