个人认为链表这部分的算法相对简单,笔试中需要ac,面试需要能说明白为什么这么操作能保证其结果正确性就好了。
链表问题考察的时间复杂度,空间复杂度稍不重要,笔试中为了过怎么都可以,面试一定要聊时间空间都最优的解法,
环形链表
法一,用set将每个节点装进去,如果下次遇到同样地址的节点,那就代表有环,如果直到遍历的指针都为空了,还没有遇到重复的节点,那就是没有环了。注意set装的是节点的地址不是节点的值,值可以重复,但是地址值是唯一的。
法二,使用双指针,slow一次一步,fast 一次两步,如果fast遇到了null,那就是没有环。
如果fast == slow 了,那就是代表他们在环里相遇了,也就是存在环。
快慢指针正确性证明
让你求个证明,总不能来个显然成立吧?(狗头
假设该链表在环出现之前有L个结点,环中有C个结点。
再假设慢指针初始化时指向的位置为a、快指针初始化时指向的位置为b,
如果二者在 t 次移动后相遇,也就是说:
(a+t-L) mod C == (b + 2 * - L ) mod C,
所以无论a、b的起始位置如何,二者总是会相遇的。
推导:
( a + t - L ) mod C == (b+2 * t-L) mod C
a+t-L-m * C == b+2 * t-L -n * C
t == (a-b) - (m - n ) * C
a=b==> t == ( n - m ) * C;
在保证m和n为正整数的情况下,上式t有正整数解。
若两个指针的步长分别为: x和y (即每走一步步长分别为: x * t 和 y * t )
( x - y ) * t== (a-b)- (m-n)*C
要是程序不陷入死循环必须满足.上面这个等式有正整数解。
比方说:假设链表是由4个结点首尾相接构成的一个圆圈(编号为0~3)
慢指针初始位置在0,每次前进1步;
快指针初始位置在3,每次前进3步;
有: (3-1) *t==(3-0)- (m-n) *4
此等式没有正整数解,所以虽然这两个指针也是- -快- -慢一前一后,但它们永远也不会相
遇!
若快慢都在 0位置,
则有(0-0) *t==(0-0)- (m-n) *4
此时,是有整数解的,所以验证是否有环与快慢指针的初始位置有关,与快慢指针之间相差几步是没有关系的,总能找到一个对应的整数,使得其成立。
验证代码:
假设现在是
链表为 1-2-3-4-5-6 6->3 存在一个环
慢指针一次一步,快指针一次三步,初始位置都在head位置
public static boolean hasCycleTest(ListNode head) {
if(head==null || head.next==null) return false;
ListNode slow = head;
ListNode fast = head;
while ( fast.next!=null && fast.next.next!=null && fast.next.next.next!=null){
System.out.println("slow.val="+slow.val+" fast.val="+fast.val);
slow=slow.next;
fast=fast.next.next.next;
if(slow==fast){
System.out.println("slow.val="+slow.val+" fast.val="+fast.val);
return true;
}
}
return false;
}
public static void main(