先说明一下单链表里有环的示意图:
即链表的尾结点指向链表中的某一个结点(结点6的next指向结点3)
判断单链表是否有环:快慢指针
定义两个指针slow, fast。slow指针一次走1个结点,fast指针一次走2个结点。如果链表中有环,那么慢指针一定会再某一个时刻追上快指针(slow == fast)。如果没有环,则快指针会第一个走到NULL。
如果单链表里有环,那么如何查找该换的入口,即从头结点开始,第一个在环里的结点(结点3):
定义两个指针,p1和p2,分两层循环来判断,外层循环p1每次走一个结点,内层循环p2每次都从头结点(步数都重新清0)开始每次走一个结点,直到p1和p2指向同一个结点,并且两者的步数不相同,那么此时两个指针指向的结点即为环的入口,如下代码可直观的解释上述过程:
**
* 查找环的起点
* @param head
* @return 返回元素的索引,从0开始。没有找到返回-1
*/
public static int findCircleEntry(Node head) {
Node p2 = head; // 总是从头开始
Node p1 = head;
int p1Steps = 0;
int p2Steps = 0;
while (null != q.next) {
p1 = p1.next;
++p1Steps;
// p2从头开始走
while (null != p2.next) {
p2 = p2.next;
++p2Steps;
// 当p1与p2指向同一个结点时
if (p1 == p2) {
// 如果走的步数不同,则这就是入口
if (p1Steps != p2Steps) {
return p1Steps - 1;
} else {
// 走的步数相同,不是入口
break;
}
}
}
p2 = head; // 回到头结点
p2Steps = 0; //每次内层循环步数都清0
}
// 其中有一个指针走到了头,说明没有环
return -1;
}