快慢指针
算法描写叙述
定义两个指针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;
}