双指针
双指针一般可以分为两类,一类是快慢指针,一类是左右指针。快慢指针通常是用来解决链表中的问题,比如典型的是判断链表中是否含有环,左右指针主要是用来解决数组/字符串的问题,比如二分搜索等 这道题主要是用快慢指针解决
快慢指针
- 初始化:两个指针都指向链表头节点head
- 前进:快指针fast在前,慢指针在后
判断链表是否有环
- 链表的特点就是每个节点都只知道下一个节点,所以一个指针是无法判断是否有环的,如果链表中不含环,那么这个指针最终会遇到空指针null,这样就知道链表到头了
- 但如果链表中有环,那就会陷入死循环,因为链表中没有null作为尾部节点。
- 所以判断单链表是否包含环,经典解法就是用双指针,一个跑得快,一个跑得慢,如果不含有环,那么跑得快的那个指针就会遇到null,但如果有环,那么快指针会超慢指针一圈,最终和慢指针相遇。
boolean isHasCycle(ListNode head){
ListNode fast,slow;
//初始化,快慢指针都指向头部
fast=slow=head;
//如果没有到达链表结尾
while(fast!==null&&fast/next!==null){
//快指针每一次走两步
fast = fast.next.next;
//慢指针每一次走一步
slow = slow.next;
//如果快指针和慢指针相遇,说明有环,返回true
}
//到达了链表尾null,说明链表不含有环,返回false
return false
}
已知链表中含有环,返回这个环的起始位置
这道题其实也不难,先说方法。
方法是:当快慢指针相遇的时候,让其中任何一个指针指向链表头,然后让这两个指针以相同的速度前进,再次相遇时所在的节点位置就是环开始的位置。
- 第一次相遇的时候,假设慢指针slow走了k步,那么快指针一定走了2k步,也就是说slow多走了k步,这k步就是环的长度
- 假设相遇点与环的起点的距离为m(橙色曲线所示),那么环的起点与头节点head的距离为k-m。这是因为慢指针走了k步,这k步包含了头节点到环的起点(绿色直线部分),以及环起点到相遇点(橙色部分),而后者的距离为m,那么环的起点与头节点head的距离就为k-m。但因为整个环的长度为k,所以从相遇点继续前进k-m步(绿色曲线部分),也会到达环起点,正好与头节点和环起点的距离是一样的。
- 所以我们只需要把快慢指针的任意一个重新指向head,然后两个指针以向相同的速度前进,在走k-m步之后,两个指针必会相遇,而相遇点就是环的起点
- 代码为:
ListNode detectCycle(ListNode head){
ListNode fast,slow;
fast = slow = head;
while(fast!=null&&fast.next!=null){
fast = fast.next.next;
slow= slow.next;
if(fast==slow) break;
}
//先让其中一个指针指向头指针
slow = head;
while(slow!=fast){
slow = slow.next;
fast = fast.next;
}
//两个指针相遇的地方就是起点
return slow;
}