7、剑指 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、双指针二次遍历
核心思路:
- 先遍历统计链表长度,记为 count ;
- 设置一个指针走 (n-k) 步,即可找到链表倒数第 k 个节点。
这也是最容易想到的解法
复杂度分析:
时间复杂度 O(N) : 需要两次遍历,因此至多为 2N 。 空间复杂度 O(1) : 需要一个辅助指针,使用常数大小的额外空间。
代码:
需要注意的是,此解法没有考虑以下几种特殊情况。
- head为空指针;
- k大于链表的长度;
- 输入的参数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 使用常数大小的额外空间。
代码:
需要注意的是,此解法没有考虑以下几种特殊情况。
- head为空指针;
- k大于链表的长度;
- 输入的参数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;
}
}