快慢指针一般都初始化指向链表的头结点 head,前进时快指针 fast 在前,慢指针 slow 在后,巧妙解决一些链表中的问题。
这应该属于链表最基本的操作了,单链表的特点是每个节点只知道下一个节点,所以一个指针的话无法判断链表中是否含有环的。
如果链表中不含环,那么这个指针最终会遇到空指针 null 表示链表到头了
但是如果链表中含有环,那么这个指针就会陷入死循环,因为环形数组中没有 null 指针作为尾部节点。
经典解法就是用两个指针,一个每次前进两步,一个每次前进一步。如果不含有环,跑得快的那个指针最终会遇到 null,说明链表不含环;如果含有环,快指针最终会超慢指针一圈,和慢指针相遇,说明链表含有环。
public class Solution {
public boolean hasCycle(ListNode head) {
ListNode fast,slow;
fast=slow=head;
while(fast!=null && fast.next!=null){
fast=fast.next.next;
slow=slow.next;
if(fast==slow){
return true;
}
}
return false;
}
}
# 这里写法要注意的是因为fast快,所有如果无环,那么肯定是fast先碰到null
# 另外因为fast=fast.next.next; 所以fast本身可能是null,fast.next也可能是null
当快慢指针相遇时,让其中任一个指针重新指向头节点,然后让它俩以相同速度前进,再次相遇时所在的节点位置就是环开始的位置。
第一次相遇时,假设慢指针 slow 走了 k 步,那么快指针 fast 一定走了 2k 步,也就是说比 slow 多走了 k 步(也就是环的长度)
设相遇点距环的起点的距离为 m,那么环的起点距头结点 head 的距离为 k - m,也就是说如果从 head 前进 k - m 步就能到达环起点,巧的是,如果从相遇点继续前进 k - m 步,也恰好到达环起点。
所以,只要我们把快慢指针中的任一个重新指向 head,然后两个指针同速前进,k - m 步后就会相遇,相遇之处就是环的起点了。
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast = head, slow = head;
//获取首次相遇时候,slow的位置
while(fast!= null && fast.next != null){
fast = fast.next.next;
slow = slow.next;
if(fast == slow){
break;
}
}
//如果快指针走到尽头,没环
if(fast == null || fast.next == null) return null;
//快指针重新出发,相遇位置就是入口位置
fast = head;
while(fast != slow){
fast = fast.next;
slow = slow.next;
}
return slow;
}
}
//之所以要在第一个循环结束之后再判断if(fast == null || fast.next == null) return null;
//是因为可能存在链表中只有一个值,那么相当于根本没有进行第一个循环
//这行就是为了处理这个特殊的可能性
我们还可以让快指针一次前进两步,慢指针一次前进一步,当快指针到达链表尽头时,慢指针就处于链表的中间位置。
当链表的长度是奇数时,slow 恰巧停在中点位置;如果长度是偶数,slow 最终的位置是中间偏右:
class Solution {
public ListNode middleNode(ListNode head) {
if(head==null){return null;}
ListNode fast,slow;
fast=slow=head;
while(slow!=null){
if(fast.next==null){
return slow;
}
if(fast.next.next==null){
return slow.next;
}
slow=slow.next;
fast=fast.next.next;
}
return null;
}
}
以上是我的代码,虽然是分类讨论了,但要注意分类讨论完成之后还是要找共性,尽量合并的写出
class Solution {
public ListNode middleNode(ListNode head) {
ListNode slow = head, fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
}
return slow;
}
}
思路还是使用快慢指针,让快指针先走 k 步,然后快慢指针开始同速前进。这样当快指针走到链表末尾 null 时,慢指针所在的位置就是倒数第 k 个链表节点(为了简化,假设 k 不会超过链表长度):
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode former = head, latter = head;
for(int i = 0; i < k; i++)
former = former.next;
while(former != null) {
former = former.next;
latter = latter.next;
}
return latter;
}
}
原文:https://www.cnblogs.com/shiji-note/p/14394711.html