目录

7、剑指 Offer 22. 链表中倒数第k个节点

一、题目

剑指 Offer 22. 链表中倒数第k个节点 难度简单

输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。

例如,一个链表有 6 个节点,从头节点开始,它们的值依次是 1、2、3、4、5、6。这个链表的倒数第 3 个节点是值为 4 的节点。

示例:

给定一个链表: 1->2->3->4->5, 和 k = 2.

返回链表 4->5.

二、解法

2.1、双指针二次遍历

核心思路:

  1. 先遍历统计链表长度,记为 count ;
  2. 设置一个指针走 (n-k) 步,即可找到链表倒数第 k 个节点。

这也是最容易想到的解法

复杂度分析:

时间复杂度 O(N) : 需要两次遍历,因此至多为 2N 。 空间复杂度 O(1) : 需要一个辅助指针,使用常数大小的额外空间。

代码:

需要注意的是,此解法没有考虑以下几种特殊情况。

  1. head为空指针;
  2. k大于链表的长度;
  3. 输入的参数k为0;
class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        ListNode cursor = head;

        // 第一次遍历链表,计算总节点数
        int count = 0;
        while (cursor != null) {
            count++;
            cursor = cursor.next;
        }

        // 第二次遍历链表,找到倒数第k个元素
        int index = count - k;
        while (index > 0) {
            head = head.next;
            index--;
        }

        return head;
    }
}

2.2、双指针单次遍历

核心思路:

不需要知道链表长度,指针 1 先走 k-1 步,然后指针 2 和指针 1 同时前进,当指针 1 指向链表最后一个元素时,指针 2 即为所求。

复杂度分析:

时间复杂度 O(N) : N 为链表长度;总体看, 指针 1 走了 N 步, 指针 走了 (N-k)(N−k) 步。 空间复杂度 O(1): 双指针 former , latter 使用常数大小的额外空间。

代码:

需要注意的是,此解法没有考虑以下几种特殊情况。

  1. head为空指针;
  2. k大于链表的长度;
  3. 输入的参数k为0;
class Solution {
    public ListNode getKthFromEnd(ListNode head, int k) {
        ListNode former = head, latter = head;

        // 让指针 1 先走 k 步
        for (int i = 0; i < k; i++)
            former = former.next;

        // 让指针1 和指针 2 同步移动, 待指针 2 走至链表尾结点时, 指针 1 指向的结点就是符合题意的答案
        while (former != null) {
            former = former.next;
            latter = latter.next;
        }

        return latter;
    }
}

REF

https://leetcode-cn.com/problems/lian-biao-zhong-dao-shu-di-kge-jie-dian-lcof/solution/mian-shi-ti-22-lian-biao-zhong-dao-shu-di-kge-j-11/