双指针分为两类:一类是“快慢指针”,一类是“左右指针”
快慢指针:主要解决链表中的问题,比如判定链表中是否包含环
左右指针:主要解决数组(或者字符串)问题,比如二分搜索
判定链表是否含有环
- 如果链表不含环,则指针会遇到空指针null,表示链表到头了
boolean hasCycle(ListNode head){
while(head != null){
head = head.next;
}
return false;
}
- 如果链表含有环,则会陷入死循环,因为环形链表中没有null指针作为尾部节点
- 双指针 ,不含环,则会遇到null;含有环,则快指针会超慢指针一圈,和慢指针相遇
boolean hasCycle(ListNode head){
ListNode fast,slow;
//初始化快、慢指针指向头节点
fast = slow = head;
while(fast != null && slow != null){
slow = slow.next;
fast = fast.next.next;
if(slow == fast){
return true;
}
}
return false;
}
已知链表有环,返回这个环的起始位置
boolean hasCycle(ListNode head){
ListNode fast,slow;
//初始化快、慢指针指向头节点
fast = slow = head;
while(fast != null && slow != null){
slow = slow.next;
fast = fast.next.next;
if(slow == fast){
break;
}
}
fast = head;
while(slow != fast){
//相同的速度前进
slow = slow.next;
fast = fast.next;
}
//两个指针的相遇的节点就是环的起点
return slow;
}
设相遇点距环的起点的距离为 m, 那么环的起点距头结点 head 的距离为 k -
m, 也就是说如果从 head 前进 k - m 步就能到达环起点。
巧的是, 如果从相遇点继续前进 k - m 步, 也恰好到达环起点。
所以, 只要我们把快慢指针中的任⼀个重新指向 head, 然后两个指针同速
前进, k - m 步后就会相遇, 相遇之处就是环的起点了。
寻找无环单链表的中点
- 暴力求解:先遍历一遍链表,算出链表的长度n,再遍历一遍链表,走n/2步则是链表的中点
- 让快指针1次前进2步, 慢指针1次前进1步, 当快指针到达链表尽头时, 慢指针就处于链表的中间位置。
while (fast != null && fast.next != null) {
fast = fast.next.next;
slow = slow.next;
}
// slow 就在中间位置
return slow;
寻找单链表的倒数第k个元素
让快指针先走 k 步, 然后快慢指针开始同速前进。 这样当快指针到链表末尾 null 时, 慢指针所在的位置就是倒数第 k
个链表节点
ListNode slow, fast;
slow = fast = head;
while (k-- > 0)
fast = fast.next;
while (fast != null) {
slow = slow.next;
fast = fast.next;
}
return slow;