数据结构与算法篇-链表的倒数第k个元素

链表的倒数第k个元素

问题描述

给定一个单链表的头节点 head,返回链表中的倒数第 k 个元素。

所谓倒数第 k 个元素,指的是距离链表尾部第 k 个位置的节点。

例如,若链表为 1 -> 2 -> 3 -> 4 -> 5k = 2,则倒数第 2 个元素为 4,即它是距离尾部第 2 个位置的节点。

注意:

  • k 遵循下标从 1 开始的规则,即 k=1 代表最后一个元素,k=2 代表倒数第二个元素,以此类推。

k 大于链表的长度,返回 null

示例

示例 1:

txt 复制代码
Input: head = [1,2,3,4,5], k = 2

Output: ListNode with value 4

Explanation: The 2nd to last element is 4

List: 1 -> 2 -> 3 -> 4 -> 5
                     ↑
                  k=2 from end

示例 2:

txt 复制代码
Input: head = [1,2], k = 1

Output: ListNode with value 2

Explanation: The 1st to last element (last element) is 2

List: 1 -> 2
           ↑
        k=1 from end

示例 3:

txt 复制代码
Input: head = [1], k = 1

Output: ListNode with value 1

Explanation: Single element list, k=1 returns the only element

示例4:

txt 复制代码
Input: head = [1,2,3], k = 5

Output: null

Explanation: k=5 is larger than list length (3), so return null

思路一:两次遍历链表

利用倒数第 k 个元素对应的正数索引刚好是 n-k,我们有:

java 复制代码
public static ListNode kthToLastNaive(ListNode head, int k) {
    ListNode current = head;
    int n = 0;
    while (current != null) {
        n++;
        current = current.next;
    }
    int pos = n - k;
    if (pos < 0) {
        return null;
    }

    if (pos == 0) {
        return head;
    }

    if (head == null) {
        return null;
    }

    current = head;
    for (int i = 0; i < pos; i++) {
        current = current.next;
    }
    return current;
}

换个思路:把先后变成同步

朴素思路的本质:时间上的先后

在思路一中:

  • 动作 A:从 0 0 0 走到 n n n(为了数数)。
  • 动作 B:等 A 结束后,再从 0 0 0 走到 n − k n-k n−k(为了定位)。

把"先后"变为"同步"我们可以思考:能不能让这两个动作同时发生?

如果你让两个人在起跑线(head)准备:

  • 快跑者 (Fast):执行动作 A(去探寻终点在哪里)。
  • 慢跑者 (Slow):执行动作 B(去定位目标位置)。

但是有一个问题:如果他们同时起跑,当 Fast 到达终点 n n n 时,Slow 也会到达 n n n。

我们希望 Slow 停在 n − k n-k n−k。

解决办法:

  • Slow 晚一点出发。
  • Fast 先走 k k k 步。
  • 此时 Fast 位于位置 k k k,Slow 位于位置 0 0 0。
  • 两人再以同样的速度同步前进。
  • Fast 走完剩余的 n − k n-k n−k 步到达终点时,Slow 也刚好走了 n − k n-k n−k 步。
  • 此时,Slow 所在的位置正是 ( 0 + n − k ) = n − k (0 + n - k) = n - k (0+n−k)=n−k。

双指针版本

java 复制代码
public static ListNode kthToLast(ListNode head, int k) {
    // Handle edge cases
    if (head == null || k <= 0) {
        return null;
    }
    // Initialize two pointers
    ListNode fast = head;
    // Move fast pointer k steps ahead
    for (int i = 0; i < k; i++) {
        // k is larger than list length
        if (fast == null) {
            return null;
        }
        fast = fast.next;
    }
    ListNode slow = head;
    // Move both pointers until fast reaches the end
    while (fast != null) {
        fast = fast.next;
        slow = slow.next;
    }
    // slow is now pointing to kth to last element
    return slow;
}

参考资料

相关推荐
haoly198916 小时前
数据结构与算法篇-链表环检测
链表环检测·双指针技术