
一、面试问题
给定一个链表,任务是删除链表的倒数第 N 个节点。
示例 1:
输入:LinkedList = 1 ->2 ->3 ->4 ->5 , N = 2
输出:1 -> 2 -> 3 -> 5
解释:删除倒数第 2 个节点(节点值为 4)后的链表为:1 -> 2 -> 3 -> 5。

示例 2:
输入:LinkedList = 7 ->8 ->4 ->3 ->2 , N = 1
输出:7 ->8 ->4 ->3
解释:删除倒数第 1 个节点(节点值为 2)后的链表为:7 -> 8 -> 4 -> 3。

二、【推荐方法-1】找到正向的等价节点 ------ 时间复杂度 O (n),空间复杂度 O (1)
(一) 解法思路
要删除倒数第 N 个节点,首先求出链表的长度 。然后,删除正向的第 (长度 - N + 1) 个节点。
按照以下步骤解题:
- 遍历链表,计算链表长度。
- 计算需要从正向删除的位置:待删节点位置 = 链表长度 − N + 1。
- 若待删位置为第 1 个节点,将头节点更新为原头节点的下一个节点。
- 遍历到目标位置的前一个节点,修改其 next 指针,跳过目标节点。
- 返回修改后的链表。
(二) 使用 6 种语言实现
1. C++
cpp
// C++ 程序:删除链表的倒数第 N 个节点
#include <iostream>
using namespace std;
// 链表节点结构
class Node {
public:
int data;
Node* next;
Node(int new_data) {
data = new_data;
next = nullptr;
}
};
// 函数:删除链表的倒数第 N 个节点
Node* removeNthFromEnd(Node* head, int N) {
// 第一步:计算链表的总长度
int length = 0;
Node* curr = head;
while (curr != nullptr) {
length++;
curr = curr->next;
}
// 第二步:计算正向需要删除的节点位置
int target = length - N + 1;
// 情况 1:要删除的是头节点
if (target == 1) {
Node* newHead = head->next;
// 释放被删除节点的内存
delete head;
return newHead;
}
// 情况 2:找到待删除节点的**前一个节点**
curr = head;
for (int i = 1; i < target - 1; i++) {
curr = curr->next;
}
// 删除目标节点,修改指针
Node* nodeToDelete = curr->next;
curr->next = curr->next->next;
delete nodeToDelete;
return head;
}
// 打印链表
void printList(Node* node) {
Node* curr = node;
while (curr != nullptr) {
cout << " " << curr->data;
curr = curr->next;
}
}
// 主函数测试
int main() {
// 构建链表: 1 -> 2 -> 3 -> 4 -> 5
Node* head = new Node(1);
head->next = new Node(2);
head->next->next = new Node(3);
head->next->next->next = new Node(4);
head->next->next->next->next = new Node(5);
int N = 2; // 删除倒数第 2 个节点
head = removeNthFromEnd(head, N);
printList(head); // 输出结果
return 0;
}
2. C
cpp
// C 程序:删除链表的倒数第 N 个节点
#include <stdio.h>
#include <stdlib.h>
// 定义链表节点结构
struct Node {
int data;
struct Node* next;
};
// 函数:删除链表的倒数第 N 个节点
struct Node* removeNthFromEnd(struct Node* head, int N) {
// 第一步:计算链表的总长度
int length = 0;
struct Node* curr = head;
while (curr != NULL) {
length++;
curr = curr->next;
}
// 第二步:计算正向需要删除的节点位置
int target = length - N + 1;
// 情况 1:需要删除的是头节点
if (target == 1) {
struct Node* newHead = head->next;
// 释放被删除节点的内存
free(head);
return newHead;
}
// 情况 2:遍历到待删除节点的**前一个节点**
curr = head;
for (int i = 1; i < target - 1; i++) {
curr = curr->next;
}
// 删除目标节点,修改指针跳过该节点
struct Node* nodeToDelete = curr->next;
curr->next = curr->next->next;
// 释放内存
free(nodeToDelete);
return head;
}
// 打印链表
void printList(struct Node* node) {
struct Node* curr = node;
while (curr != NULL) {
printf(" %d", curr->data);
curr = curr->next;
}
}
// 创建新节点
struct Node* createNode(int new_data) {
struct Node* new_node
= (struct Node*)malloc(sizeof(struct Node));
new_node->data = new_data;
new_node->next = NULL;
return new_node;
}
// 主函数测试
int main() {
// 创建链表: 1 -> 2 -> 3 -> 4 -> 5
struct Node* head = createNode(1);
head->next = createNode(2);
head->next->next = createNode(3);
head->next->next->next = createNode(4);
head->next->next->next->next = createNode(5);
int N = 2; // 删除倒数第 2 个节点
head = removeNthFromEnd(head, N);
printList(head); // 输出结果
return 0;
}
3. Java
java
// Java 程序:删除链表的倒数第 N 个节点
class Node {
int data;
Node next;
// 构造函数:创建新节点
Node(int new_data) {
data = new_data;
next = null;
}
}
// 主类:包含删除节点的函数
public class DSA {
// 函数:删除链表的倒数第 N 个节点
static Node removeNthFromEnd(Node head, int N) {
// 第一步:计算链表的总长度
int length = 0;
Node curr = head;
while (curr != null) {
length++;
curr = curr.next;
}
// 第二步:计算正向需要删除的节点位置
int target = length - N + 1;
// 情况 1:要删除的是头节点,直接返回下一个节点
if (target == 1) {
return head.next;
}
// 情况 2:遍历到待删除节点的**前一个节点**
curr = head;
for (int i = 1; i < target - 1; i++) {
curr = curr.next;
}
// 删除目标节点:将前一个节点的 next 跳过目标节点
curr.next = curr.next.next;
return head;
}
// 打印链表
static void printList(Node node) {
Node curr = node;
while (curr != null) {
System.out.print(" " + curr.data);
curr = curr.next;
}
}
// 主函数:测试代码
public static void main(String[] args) {
// 创建链表: 1 -> 2 -> 3 -> 4 -> 5
Node head = new Node(1);
head.next = new Node(2);
head.next.next = new Node(3);
head.next.next.next = new Node(4);
head.next.next.next.next = new Node(5);
int N = 2; // 删除倒数第 2 个节点
head = removeNthFromEnd(head, N);
printList(head); // 输出结果
}
}
4. Python
python
# Python3 程序:删除链表的倒数第 N 个节点
class Node:
def __init__(self, new_data):
self.data = new_data
self.next = None
# 函数:删除链表的倒数第 N 个节点
def remove_nth_from_end(head, N):
# 第一步:计算链表的总长度
length = 0
curr = head
while curr is not None:
length += 1
curr = curr.next
# 第二步:计算正向需要删除的节点位置
target = length - N + 1
# 情况 1:要删除的是头节点,直接返回下一个节点
if target == 1:
return head.next
# 情况 2:遍历到待删除节点的**前一个节点**
curr = head
for _ in range(target - 2):
curr = curr.next
# 删除目标节点:将前一个节点的 next 跳过目标节点
curr.next = curr.next.next
return head
# 打印链表
def print_list(node):
curr = node
while curr is not None:
print(f" {curr.data}", end="")
curr = curr.next
print()
# 主函数测试
if __name__ == "__main__":
# 创建链表: 1 -> 2 -> 3 -> 4 -> 5
head = Node(1)
head.next = Node(2)
head.next.next = Node(3)
head.next.next.next = Node(4)
head.next.next.next.next = Node(5)
N = 2 # 删除倒数第 2 个节点
head = remove_nth_from_end(head, N)
print_list(head)
5. C#
cs
// C# 程序:删除链表的倒数第 N 个节点
using System;
class Node {
public int Data;
public Node next;
// 构造函数:创建新节点
public Node(int newData) {
Data = newData;
next = null;
}
}
// 包含删除函数的类
class GfG {
static Node RemoveNthFromEnd(Node head, int N) {
// 第一步:计算链表的长度
int length = 0;
Node curr = head;
while (curr != null) {
length++;
curr = curr.next;
}
// 第二步:计算正向需要删除的节点位置
int target = length - N + 1;
// 情况 1:要删除的是头节点,直接返回下一个节点
if (target == 1) {
return head.next;
}
// 情况 2:遍历到目标节点的**前一个节点**
curr = head;
for (int i = 1; i < target - 1; i++) {
curr = curr.next;
}
// 删除目标节点:跳过当前节点的下一个节点
curr.next = curr.next.next;
return head;
}
// 打印链表
static void PrintList(Node node) {
Node curr = node;
while (curr != null) {
Console.Write(" " + curr.Data);
curr = curr.next;
}
Console.WriteLine();
}
// 主函数:测试
static void Main() {
// 创建链表: 1 -> 2 -> 3 -> 4 -> 5
Node head = new Node(1);
head.next = new Node(2);
head.next.next = new Node(3);
head.next.next.next = new Node(4);
head.next.next.next.next = new Node(5);
int N = 2; // 删除倒数第 2 个节点
head = RemoveNthFromEnd(head, N);
PrintList(head);
}
}
6. JavaScript
javascript
// JavaScript 程序:删除链表的倒数第 N 个节点
class Node {
constructor(newData) {
this.data = newData;
this.next = null;
}
}
// 函数:删除链表的倒数第 N 个节点
function removeNthFromEnd(head, N) {
// 第一步:计算链表的总长度
let length = 0;
let curr = head;
while (curr !== null) {
length++;
curr = curr.next;
}
// 第二步:计算正向需要删除的节点位置
let target = length - N + 1;
// 情况 1:要删除的是头节点,直接返回下一个节点
if (target === 1) {
return head.next;
}
// 情况 2:遍历到目标节点的**前一个节点**
curr = head;
for (let i = 1; i < target - 1; i++) {
curr = curr.next;
}
// 删除目标节点:将前一个节点的 next 跳过目标节点
curr.next = curr.next.next;
return head;
}
// 打印链表
function printList(node) {
let curr = node;
while (curr !== null) {
process.stdout.write(" " + curr.data);
curr = curr.next;
}
console.log();
}
// 主函数测试
// 创建链表: 1 -> 2 -> 3 -> 4 -> 5
let head = new Node(1);
head.next = new Node(2);
head.next.next = new Node(3);
head.next.next.next = new Node(4);
head.next.next.next.next = new Node(5);
let N = 2; // 删除倒数第 2 个节点
head = removeNthFromEnd(head, N);
printList(head);
(三) 代码输出和算法复杂度
输出:
1 2 3 5
时间复杂度:O(n)。
空间复杂度:O(1)。
三、【推荐解法-2】使用快慢指针 ------ 时间复杂度 O (n),空间复杂度 O (1)
(一) 解法思路
核心思路是:先将快指针向前移动 N 步 ,然后同时移动快指针和慢指针,直到快指针到达链表末尾。此时慢指针恰好位于待删除节点的前一个节点,我们只需修改它的 next 指针,跳过目标节点即可完成删除。
(二) 使用 6 种语言实现
1. C++
cpp
// C++ 程序:使用快慢指针删除链表的倒数第 N 个节点
#include <iostream>
using namespace std;
// 链表节点类
class Node {
public:
int data;
Node* next;
Node(int new_data) {
data = new_data;
next = nullptr;
}
};
// 函数:删除倒数第 N 个节点(快慢指针法)
Node* removeNthFromEnd(Node* head, int N) {
// 初始化快慢指针,都指向头节点
Node* fast = head;
Node* slow = head;
// 第一步:让快指针先走 N 步
for (int i = 0; i < N; i++) {
if (fast == nullptr) return head; // 边界处理
fast = fast->next;
}
// 第二步:如果快指针为空,说明要删除的是头节点
if (fast == nullptr) {
Node* newHead = head->next;
delete head; // 释放内存
return newHead;
}
// 第三步:快慢指针一起移动,直到快指针到达最后一个节点
while (fast->next != nullptr) {
fast = fast->next;
slow = slow->next;
}
// 第四步:删除慢指针的下一个节点(即目标节点)
Node* nodeToDelete = slow->next;
slow->next = slow->next->next;
delete nodeToDelete;
return head;
}
// 打印链表
void printList(Node* node) {
Node* curr = node;
while (curr != nullptr) {
cout << " " << curr->data;
curr = curr->next;
}
}
// 主函数测试
int main() {
// 构建链表: 1 -> 2 -> 3 -> 4 -> 5
Node* head = new Node(1);
head->next = new Node(2);
head->next->next = new Node(3);
head->next->next->next = new Node(4);
head->next->next->next->next = new Node(5);
int N = 2; // 删除倒数第 2 个节点
head = removeNthFromEnd(head, N);
printList(head); // 输出结果
return 0;
}
2. C
cpp
// C 程序:使用快慢指针删除链表的倒数第 N 个节点
#include <stdio.h>
#include <stdlib.h>
struct Node {
int data;
struct Node* next;
};
// 函数:删除链表倒数第 N 个节点(快慢指针法)
struct Node* removeNthFromEnd(struct Node* head, int N) {
// 定义快慢指针,初始都指向头节点
struct Node* fast = head;
struct Node* slow = head;
// 快指针先向前走 N 步
for (int i = 0; i < N; i++) {
if (fast == NULL) return head;
fast = fast->next;
}
// 如果快指针为空,说明要删除的是头节点
if (fast == NULL) {
struct Node* newHead = head->next;
free(head);
return newHead;
}
// 快慢指针一起移动,直到快指针到达链表末尾
while (fast->next != NULL) {
fast = fast->next;
slow = slow->next;
}
// 删除慢指针的下一个节点(即倒数第 N 个节点)
struct Node* nodeToDelete = slow->next;
slow->next = slow->next->next;
free(nodeToDelete);
return head;
}
// 打印链表
void printList(struct Node* node) {
struct Node* curr = node;
while (curr != NULL) {
printf(" %d", curr->data);
curr = curr->next;
}
}
// 创建新节点
struct Node* createNode(int new_data) {
struct Node* new_node
= (struct Node*)malloc(sizeof(struct Node));
new_node->data = new_data;
new_node->next = NULL;
return new_node;
}
// 主函数测试
int main() {
// 创建链表: 1 -> 2 -> 3 -> 4 -> 5
struct Node* head = createNode(1);
head->next = createNode(2);
head->next->next = createNode(3);
head->next->next->next = createNode(4);
head->next->next->next->next = createNode(5);
int N = 2;
head = removeNthFromEnd(head, N);
printList(head);
return 0;
}
3. Java
java
// Java 程序:使用快慢指针删除链表的倒数第 N 个节点
class Node {
int data;
Node next;
Node(int new_data) {
data = new_data;
next = null;
}
}
// 主类:包含删除方法
public class DSA {
// 函数:删除链表的倒数第 N 个节点(快慢指针法)
static Node removeNthFromEnd(Node head, int N) {
// 初始化快慢指针,都指向头节点
Node fast = head;
Node slow = head;
// 第一步:让快指针先走 N 步
for (int i = 0; i < N; i++) {
if (fast == null)
return head; // 边界处理:N 超出链表长度
fast = fast.next;
}
// 第二步:如果快指针为空,说明要删除的是头节点
if (fast == null) {
return head.next;
}
// 第三步:快慢指针一起移动,直到快指针到达最后一个节点
while (fast.next != null) {
fast = fast.next;
slow = slow.next;
}
// 第四步:删除目标节点(慢指针的下一个节点)
slow.next = slow.next.next;
return head;
}
// 打印链表
static void printList(Node node) {
Node curr = node;
while (curr != null) {
System.out.print(" " + curr.data);
curr = curr.next;
}
}
// 主函数测试
public static void main(String[] args) {
// 创建链表: 1 -> 2 -> 3 -> 4 -> 5
Node head = new Node(1);
head.next = new Node(2);
head.next.next = new Node(3);
head.next.next.next = new Node(4);
head.next.next.next.next = new Node(5);
int N = 2; // 删除倒数第 2 个节点
head = removeNthFromEnd(head, N);
printList(head);
}
}
4. Python
python
# Python3 程序:使用快慢指针删除链表的倒数第 N 个节点
class Node:
def __init__(self, new_data):
self.data = new_data
self.next = None
# 函数:删除链表的倒数第 N 个节点(快慢指针法)
def remove_nth_from_end(head, N):
# 初始化快慢指针,都指向头节点
fast = head
slow = head
# 第一步:让快指针先走 N 步
for _ in range(N):
if fast is None:
return head # 边界处理
fast = fast.next
# 第二步:如果快指针为空,说明要删除的是头节点
if fast is None:
return head.next
# 第三步:快慢指针一起移动,直到快指针到达最后一个节点
while fast.next is not None:
fast = fast.next
slow = slow.next
# 第四步:删除慢指针的下一个节点(目标节点)
slow.next = slow.next.next
return head
# 打印链表
def print_list(node):
curr = node
while curr is not None:
print(f" {curr.data}", end="")
curr = curr.next
print()
# 主函数测试
if __name__ == "__main__":
# 创建链表: 1 -> 2 -> 3 -> 4 -> 5
head = Node(1)
head.next = Node(2)
head.next.next = Node(3)
head.next.next.next = Node(4)
head.next.next.next.next = Node(5)
N = 2 # 删除倒数第 2 个节点
head = remove_nth_from_end(head, N)
print_list(head)
5. C#
cs
// C# 程序:使用快慢指针删除链表的倒数第 N 个节点
using System;
class Node {
public int Data;
public Node next;
// 构造函数:创建新节点
public Node(int newData) {
Data = newData;
next = null;
}
}
// 包含删除函数的类
class DSA {
// 函数:删除链表倒数第 N 个节点(快慢指针法)
static Node RemoveNthFromEnd(Node head, int N) {
// 初始化快慢指针,都指向头节点
Node fast = head;
Node slow = head;
// 第一步:快指针先向前移动 N 步
for (int i = 0; i < N; i++) {
if (fast == null) return head;
fast = fast.next;
}
// 第二步:如果快指针为空,说明要删除的是头节点
if (fast == null) {
return head.next;
}
// 第三步:快慢指针一起移动,直到快指针到达链表末尾
while (fast.next != null) {
fast = fast.next;
slow = slow.next;
}
// 第四步:删除慢指针的下一个节点(目标节点)
slow.next = slow.next.next;
return head;
}
// 打印链表
static void PrintList(Node node) {
Node curr = node;
while (curr != null) {
Console.Write(" " + curr.Data);
curr = curr.next;
}
Console.WriteLine();
}
// 主函数:测试代码
static void Main() {
// 创建链表: 1 -> 2 -> 3 -> 4 -> 5
Node head = new Node(1);
head.next = new Node(2);
head.next.next = new Node(3);
head.next.next.next = new Node(4);
head.next.next.next.next = new
6. JavaScript
javascript
// JavaScript 程序:使用快慢指针删除链表的倒数第 N 个节点
class Node {
constructor(newData) {
this.data = newData;
this.next = null;
}
}
// 函数:删除链表倒数第 N 个节点(快慢指针法)
function removeNthFromEnd(head, N) {
// 初始化快慢指针,都指向头节点
let fast = head;
let slow = head;
// 第一步:快指针先向前移动 N 步
for (let i = 0; i < N; i++) {
if (fast === null) return head;
fast = fast.next;
}
// 第二步:如果快指针为空,说明要删除的是头节点
if (fast === null) {
return head.next;
}
// 第三步:快慢指针一起移动,直到快指针到达链表末尾
while (fast.next !== null) {
fast = fast.next;
slow = slow.next;
}
// 第四步:删除慢指针的下一个节点(目标节点)
slow.next = slow.next.next;
return head;
}
// 打印链表
function printList(node) {
let curr = node;
while (curr !== null) {
process.stdout.write(" " + curr.data);
curr = curr.next;
}
console.log();
}
// 主函数测试
// 创建链表: 1 -> 2 -> 3 -> 4 -> 5
let head = new Node(1);
head.next = new Node(2);
head.next.next = new Node(3);
head.next.next.next = new Node(4);
head.next.next.next.next = new Node(5);
let N = 2; // 删除倒数第 2 个节点
head = removeNthFromEnd(head, N);
printList(head);
(三) 代码输出和算法复杂度
输出:
1 2 3 5
时间复杂度:O(n)。
空间复杂度:O(1)。
