快慢指针

160 阅读2分钟

141. 环形链表

public class Solution {
    public boolean hasCycle(ListNode head) {
        ListNode slow=head,fast=head;
        while(fast!=null && fast.next!=null){
            fast=fast.next.next;
            slow=slow.next;
            if(slow==fast) return true;
        }
        return false;
    }
}

142. 环形链表 II

快指针与满指针相遇时,快指针所走的距离a+n(b+c)+ba+(n+1)b+nc

又快指针走过的距离是慢指针的2倍,因此a+(n+1)b+nc=2(a+b)即a=c+(n-1)(b+c)

我们就知道了从相遇点到入环点的距离加上 n−1 圈的环长,恰好等于从链表头部到入环点的距离。

image.png 代码

public class Solution {
    public ListNode detectCycle(ListNode head) {
        ListNode fast=head,slow=head;
        //找到相遇处
        while(fast!=null && fast.next!=null){
            fast=fast.next.next;
            slow=slow.next;
            if(slow==fast) break;
        }
        if(fast==null || fast.next==null) return null;
        fast=head;
        while(fast!=slow){
            fast=fast.next;
            slow=slow.next;
        }
        return fast;
    }
}

求环的长度

  • 思路很简单,相遇时把节点记录下来,然后让他走一圈,记录步数就是环的长度了

求链表总长度

  • 上一问拿到了环的长度+起点到环的入口的长度即可
  • 起点到环的入口的长度?
    • 上一问拿到了入口,从起点再走一趟就好了

234. 回文链表

  • 快指针走到末端时,慢指针更好在中间(快指针一次两步,慢指针一次走一步)
  • 慢指针一边走一边翻转
  • 当快指针到终点的时候就可以开始比较了
class Solution {
    public boolean isPalindrome(ListNode head) {
        ListNode pre=null;
        ListNode slow=head;
        ListNode fast=head;
        //翻转时中用的中间节点
        ListNode temp;
        while(fast!=null && fast.next!=null){
            //翻转
            temp=slow.next;
            if(pre!=null) slow.next=pre;
            pre=slow;
            slow=temp;
            //走两步
            fast=fast.next.next;
        }
        //节点数为奇数,从下一个开始比较
        if(fast!=null) slow=slow.next;
        while(slow!=null) {
            if(slow.val!=pre.val) return false;
            slow=slow.next;
            pre=pre.next;
        }
        return true;
    }
}

2095. 删除链表的中间节点

  • 虚拟一个头结点,用来避免奇偶带来的影响,当快指针走到尾时,慢指针一定走到所谓中间节点的前一个节点
class Solution {
    public ListNode deleteMiddle(ListNode head) {
        ListNode dummy=new ListNode(-1);
        dummy.next=head;
        ListNode slow=dummy,fast=dummy;
        while(fast.next!=null&&fast.next.next!=null){
            slow=slow.next;
            fast=fast.next.next;
        }
        slow.next=slow.next.next;
        return dummy.next;
    }
}

206. 反转链表

  • 递归法
class Solution {
    public ListNode reverseList(ListNode head) {
        if(head==null||head.next==null) return head;
        ListNode ret=reverseList(head.next);
        head.next.next=head;
        head.next=null;
        return ret;
    }
   
}
  • 迭代法
class Solution {
    public ListNode reverseList(ListNode head) {
        
        ListNode prev = null; //前指针节点
        ListNode curr = head; //当前指针节点
        //每次循环,都将当前节点指向它前面的节点,然后当前节点和前节点后移
        while (curr != null) {
            ListNode nextTemp = curr.next; //临时节点,暂存当前节点的下一节点,用于后移
            curr.next = prev; //将当前节点指向它前面的节点
            prev = curr; //前指针后移
            curr = nextTemp; //当前指针后移
        }
        return prev;
   }
   
}

143. 重排链表

image.png

class Solution {
    public void reorderList(ListNode head) {
        ListNode dummy=new ListNode(-1);
        dummy.next=head;
        ListNode slow=head;
        ListNode fast=head;
        while(fast.next!=null&&fast.next.next!=null){
            slow=slow.next;
            fast=fast.next.next;
        }
        ListNode slowNext=slow.next;
        slow.next=null;
        ListNode node2=reverseList(slowNext);
        ListNode node1=dummy.next;
        while(node2!=null){
            ListNode temp1=node1.next;
            node1.next=node2;
            node2=node2.next;
            node1=node1.next;
            node1.next=temp1;
            node1=node1.next;
        }
    }
    ListNode reverseList(ListNode node){
        if(node==null||node.next==null) return node;
        ListNode ret=reverseList(node.next);
        node.next.next=node;
        node.next=null;
        return ret;
    }
}