链表题目:链表的中间结点

文章目录

  • 题目
    • 标题和出处
    • 难度
    • 题目描述
      • 要求
      • 示例
      • 数据范围
  • 解法一
    • 思路和算法
    • 代码
    • 复杂度分析
  • 解法二
    • 思路和算法
    • 证明
    • 代码
    • 复杂度分析

题目

标题和出处

标题:链表的中间结点

出处:876. 链表的中间结点

难度

3 级

题目描述

要求

给定一个头结点为 head \texttt{head} head 的非空单链表,返回链表的中间结点。

如果有两个中间结点,则返回第二个中间结点。

示例

示例 1:

示例 1

输入: head   =   [1,2,3,4,5] \texttt{head = [1,2,3,4,5]} head = [1,2,3,4,5]
输出: [3,4,5] \texttt{[3,4,5]} [3,4,5]

示例 2:

示例 2

输入: head   =   [1,2,3,4,5,6] \texttt{head = [1,2,3,4,5,6]} head = [1,2,3,4,5,6]
输出: [4,5,6] \texttt{[4,5,6]} [4,5,6]
解释:由于该链表有两个中间结点,值分别为 3 \texttt{3} 3 4 \texttt{4} 4,我们返回第二个结点。

数据范围

  • 链表中结点的数目在区间 [1,   100] \texttt{[1, 100]} [1, 100]
  • 1 ≤ Node.val ≤ 100 \texttt{1} \le \texttt{Node.val} \le \texttt{100} 1Node.val100

解法一

思路和算法

由于链表的中间结点的编号和链表的结点数量相关,因此可以首先遍历链表得到链表的结点数量,然后计算链表的中间结点的编号,再定位到链表的中间结点。

假设链表的结点数量是 n n n,头结点到尾结点的编号依次是 1 1 1 n n n。当 n n n 分别是奇数和偶数时,链表的中间结点的编号如下:

  • n n n 是奇数时,链表有一个中间结点,其编号是 n + 1 2 = n − 1 2 + 1 \dfrac{n+1}{2}=\dfrac{n-1}{2}+1 2n+1=2n1+1

  • n n n 是偶数时,链表有两个中间结点,第二个中间结点的编号是 n 2 + 1 \dfrac{n}{2}+1 2n+1

n n n 是偶数时, n n n 个结点的链表和 n + 1 n+1 n+1 个结点的链表的中间结点的编号相同。对任意正整数 n n n,中间结点的编号可以表示成 ⌊ n 2 ⌋ + 1 \Big\lfloor\dfrac{n}{2}\Big\rfloor+1 2n+1

因此在得到链表的结点数量 n n n 之后,从头结点开始,向后移动 ⌊ n 2 ⌋ \Big\lfloor\dfrac{n}{2}\Big\rfloor 2n 次,即可定位到链表的中间结点。

代码

class Solution {
    public ListNode middleNode(ListNode head) {
        int n = 0;
        ListNode temp = head;
        while (temp != null) {
            n++;
            temp = temp.next;
        }
        int middleCount = n / 2 + 1;
        ListNode middle = head;
        for (int i = 1; i < middleCount; i++) {
            middle = middle.next;
        }
        return middle;
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是链表的长度。需要遍历链表一次得到链表的结点数量,然后遍历链表定位到链表的中间结点。

  • 空间复杂度: O ( 1 ) O(1) O(1)

解法二

思路和算法

得到链表的中间结点的另一种解法是快慢指针,使用快慢指针解法不需要事先知道链表的结点数量。

快慢指针的做法是:创建快指针和慢指针,初始时都指向头结点,然后每次将快指针往后移动 2 2 2 步,将慢指针往后移动 1 1 1 步,直到快指针到达链表的尾结点或者空结点(尾结点的后一个结点为空结点),此时慢指针指向的结点就是链表的中间结点。

证明

对于快慢指针解法的正确性证明,分别考虑链表的结点数量 n n n 是奇数和偶数的情况。

  • n n n 是奇数时,快指针会移动到链表的尾结点,一共移动 n − 1 n-1 n1 步,慢指针移动 n − 1 2 \dfrac{n-1}{2} 2n1 步,因此当移动结束时,慢指针位于第 n + 1 2 \dfrac{n+1}{2} 2n+1 个结点,是链表的中间结点。

  • n n n 是偶数时,快指针会移动到链表的空结点,一共移动 n n n 步,慢指针移动 n 2 \dfrac{n}{2} 2n 步,因此当移动结束时,慢指针位于第 n 2 + 1 \dfrac{n}{2}+1 2n+1 个结点,是链表的中间结点。

代码

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;
    }
}

复杂度分析

  • 时间复杂度: O ( n ) O(n) O(n),其中 n n n 是链表的长度。快指针遍历链表一次,慢指针遍历链表的前半段一次。

  • 空间复杂度: O ( 1 ) O(1) O(1)

你可能感兴趣的:(数据结构和算法,#,链表,链表)