一、单链表的倒数第 k 个节点
1.思路
首先,我们先让一个指针p1指向链表的头节点head,走k步;
然后再用一个指针p2指向链表头节点head,让p1和p2同时向前走,p1走到链表末尾的空指针时走了n - k步,p2也走了n - k步,也就恰好到达了链表的倒数第k个节点
2.代码
// 返回链表的倒数第 k 个节点
ListNode findFromEnd(ListNode head, int k) {
ListNode p1 = head;
// p1 先走 k 步
for (int i = 0; i < k; i++) {
p1 = p1.next;
}
ListNode p2 = head;
// p1 和 p2 同时走 n - k 步
while (p1 != null) {
p2 = p2.next;
p1 = p1.next;
}
// p2 现在指向第 n - k 个节点
return p2;
}
二、删除链表的倒数第 N 个结点
1.思路
使用虚拟头结点的技巧,是为了防止出现空指针的情况,比如说链表总共有 5 个节点,题目就让你删除倒数第 5 个节点,也就是第一个节点,那按照算法逻辑,应该首先找到倒数第 6 个节点。但第一个节点前面已经没有节点了,这就会出错。
2.代码
// 主函数
public ListNode removeNthFromEnd(ListNode head, int n) {
// 虚拟头节点
ListNode dummy = new ListNode(-1);
dummy.next = head;
// 删除倒数第 n 个,要先找倒数第 n + 1 个节点
ListNode x = findFromEnd(dummy, n + 1);
// 删掉倒数第 n 个节点
x.next = x.next.next;
return dummy.next;
}
private ListNode findFromEnd(ListNode head, int k) {
// 代码见上文
}