1 题目描述
输入一个链表,输出该链表中倒数第k个节点。为了符合大多数人的习惯,本题从1开始计数,即链表的尾节点是倒数第1个节点。例如,一个链表有6个节点,从头节点开始,它们的值依次是1、2、3、4、5、6。这个链表的倒数第3个节点是值为4的节点。
示例:
给定一个链表: 1->2->3->4->5, 和 k = 2.
返回链表 4->5.
2 解题思路
分别将指针p1
和p2
指向链表头,设置一个索引idx
从1开始,这时进入第一个循环并用p1
遍历链表,每次循环将idx
加1,直到idx
等于k
,此时结束第一个循环,然后进入第二个循环,同时用p1
和p2
开始遍历链表,当p1.next
等于null
时结束循环,此时p2
就是该链表中倒数第k个节点。
要注意代码的鲁棒性:
- 输入的
head
为空指针。由于代码试图访问空指针指向的内存,从而导致程序崩溃; - 输入的以
head
为头节点的链表的节点总数少于k。由于在循环中会在链表上向前走k-1步,仍然会由于空指针而造成程序崩溃。 - 输入的参数k为0。由于k是一个无符号整数,那么在循环中k-1得到的将不是-1,而是4294967295(无符号的0xFFFFFFFF)。因此,循环执行的次数远远超过我们的预计,同样也会造成程序的崩溃。
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode(int x) { val = x; }
* }
*/
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
if (head == null || k == 0) {
return null;
}
ListNode p1;
ListNode p2;
int idx = 1;
p1 = head;
p2 = head;
while (idx < k && p1 != null) {
p1 = p1.next;
idx++;
}
if (p1 == null) {
return null;
}
while (p1.next != null) {
p1 = p1.next;
p2 = p2.next;
}
return p2;
}
}
复杂度分析
- 时间复杂度O(N):N为链表长度;总体看,
p1
走了N步,p2
走了N-k步; - 空间复杂度O(1):双指针
p1
和p2
使用常数大小的额外空间。
3 其他解题思路
3.1 方法1:双指针(只需遍历一次链表)
和上述方法相同,代码更简洁:
class Solution {
public ListNode getKthFromEnd(ListNode head, int k) {
ListNode former = head, latter = head;
for(int i = 0; i < k; i++) {
if(former == null) return null;
former = former.next;
}
while(former != null) {
former = former.next;
latter = latter.next;
}
return latter;
}
}
3.2 方法2:单指针(需要遍历两次链表)
- 先遍历统计链表长度,记为n;
- 设置一个指针走(n-k)步,即可找到链表倒数第k个节点。