你真的会找链表“中点”么?

101 阅读4分钟

做过链表题目的小伙伴对于如何寻找单链表的“中点”应该不陌生。许多链表题目的“破题点”就在于找到单链表的中间结点

下面我们就来看四道非常相似的寻找单链表中点的题目:

给定一个带有头结点 head 的非空单链表,

  1. 若链表为奇数长度返回中点,若链表为偶数长度返回上中点

  2. 若链表为奇数长度返回中点,若链表为偶数长度返回下中点

  3. 若链表为奇数长度返回中点前一个,若链表为偶数长度返回上中点前一个

  4. 若链表为奇数长度返回中点前一个,若链表为偶数长度返回下中点前一个

若链表长度为偶数,则

上中点:中间两个结点中的第一个结点

下中点:中间两个结点中的第二个结点

这四道题均采用 快慢指针 的思想进行解答,即慢指针 slow 走一步,快指针 fast 走两步。

  • 但想要完美做出这四道题,关键在于区分好不同情况之间的边界判断条件

问题1

奇数返回中点,偶数返回上中点

public static Node midOrUpMidNode(Node head) {
    if (head == null || head.next == null || head.next.next == null) {
        return head;
    }
    Node slow = head.next;
    Node fast = head.next.next;
    while (fast.next != null && fast.next.next != null) {
        slow = slow.next;
        fast = fast.next.next;
    }
    return slow;
}

该问题的初始条件判断是三种为空的情况:

1.1.jpg

不难看出,当链表为空,或仅有 1 个或 2 个结点时,中间结点均为头指针 head 所指向的结点。

当结点的个数超过 3 个时,快慢指针初始设置为:slow = head.next; fast = head.next.next;

1.2.jpg

当链表结点个数为奇数,循环结束快慢指针的指向情况为: 1.3.jpg

当链表结点个数为偶数,循环结束快慢指针的指向情况为:

1.4.jpg

此时慢指针 slow 指向了偶数结点的上中点

问题2

奇数返回中点,偶数返回下中点

public static Node midOrDownMidNode(Node head) {
    if (head == null || head.next == null) {
        return head;
    }
    Node slow = head.next;
    Node fast = head.next;
    while (fast.next != null && fast.next.next != null) {
        slow = slow.next;
        fast = fast.next.next;
    }
    return slow;
}

该问题的初始条件判断是两种为空的情况:

2.1.jpg

当链表为空,或仅有 1 个结点时,中间结点均为头指针 head 所指向的结点。

当结点的个数超过 3 个时,快慢指针初始设置为:slow = head.next; fast = head.next; 2.2.jpg

当链表结点个数为奇数,循环结束快慢指针的指向情况为:

2.3.jpg

当链表结点个数为偶数,循环结束快慢指针的指向情况为:

2.4.jpg

此时慢指针 slow 指向了偶数结点的下中点

问题3

奇数返回中点前一个,偶数返回上中点前一个

public static Node midOrUpMidPreNode(Node head) {
    if (head == null || head.next == null || head.next.next == null) {
        return null;
    }
    Node slow = head;
    Node fast = head.next.next;
    while (fast.next != null && fast.next.next != null) {
        slow = slow.next;
        fast = fast.next.next;
    }
    return slow;
}

问题 3 与问题 1 的初始判断条件一致:

1.1.jpg 当链表为空,或仅有 1 个或 2 个结点时,中点前一个均为头指针 head 。

当结点的个数超过 3 个时,快慢指针初始设置为:slow = head; fast = head.next.next;

3.2.jpg

当链表结点个数为奇数,循环结束快慢指针的指向情况为:

3.3.jpg 此时慢指针 slow 指向了奇数结点的中点前一个

当链表结点个数为偶数,循环结束快慢指针的指向情况为:

3.4.jpg

此时慢指针 slow 指向了偶数结点的上中点的前一个

问题4

奇数返回中点前一个,偶数返回下中点前一个

public static Node midOrDownMidPreNode(Node head) {
    if (head == null || head.next == null) {
        return null;
    }
    if (head.next.next == null) {
        return head;
    }
    Node slow = head;
    Node fast = head.next;
    while (fast.next != null && fast.next.next != null) {
        slow = slow.next;
        fast = fast.next.next;
    }
    return slow;
}

问题 4 的初始判断条件略有不同:

2.1.jpg

当链表为空,或仅有 1 个结点时,下中点前一个均为 null 。

当链表仅有 2 个结点时,下中点前一个是第一个结点,即 head 。

当结点的个数超过 3 个时,快慢指针初始设置为:slow = head; fast = head.next;

当链表结点个数为奇数,循环结束快慢指针的指向情况为:

此时慢指针 slow 指向了奇数结点的中点前一个

当链表结点个数为偶数,循环结束快慢指针的指向情况为:

此时慢指针 slow 指向了偶数结点的下中点的前一个