反转链表

题目描述
给你单链表的头节点 head,请你反转链表,并返回反转后的链表的头节点。

输入格式
输入共 2 行:

  • 第一行包含一个整数 n,表示链表的长度。
  • 第二行包含 n 个整数,表示链表的节点值,按链表顺序给出。

输出格式
输出共 1 行,包含 n 个整数,表示反转后的链表的节点值,按链表顺序给出。

数据范围
链表中节点的数目在范围 [0, 5000] 内
-5000 ≤ Node.val ≤ 5000

输入样例1:

1
2
5
1 2 3 4 5

输出样例1:

1
5 4 3 2 1

输入样例2:

1
2
2
1 2

输出样例2:

1
2 1

输入样例3:

1
2
1
1

输出样例3:

1
1

注意事项

  • 可以使用迭代或递归的方法来反转链表。
  • 迭代方法的时间复杂度为O(n),空间复杂度为O(1)。
  • 递归方法的时间复杂度为O(n),空间复杂度为O(n),因为递归调用会使用栈空间。
  • 在本题中,为了方便输入输出,我们将使用数组来模拟链表的输入,并将反转后的链表转换为数组输出。

代码实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
#include <iostream>
#include <vector>
using namespace std;

// 定义链表节点结构
struct ListNode {
int val;
ListNode *next;
ListNode() : val(0), next(nullptr) {}
ListNode(int x) : val(x), next(nullptr) {}
ListNode(int x, ListNode *next) : val(x), next(next) {}
};

// 方法一:迭代反转链表
ListNode* reverseListIterative(ListNode* head)
{
ListNode* prev = nullptr;
ListNode* curr = head;

while (curr != nullptr) {
ListNode* nextTemp = curr->next; // 保存下一个节点
curr->next = prev; // 反转当前节点的指针
prev = curr; // 移动prev指针
curr = nextTemp; // 移动curr指针
}

return prev; // prev现在是新的头节点
}

// 方法二:递归反转链表
ListNode* reverseListRecursive(ListNode* head)
{
// 基本情况:空链表或只有一个节点
if (head == nullptr || head->next == nullptr) {
return head;
}

// 递归反转剩余部分
ListNode* newHead = reverseListRecursive(head->next);

// 反转当前节点和下一个节点的连接
head->next->next = head;
head->next = nullptr;

return newHead;
}

// 从数组创建链表
ListNode* createList(vector<int>& nums)
{
if (nums.empty()) {
return nullptr;
}

ListNode* head = new ListNode(nums[0]);
ListNode* current = head;

for (int i = 1; i < nums.size(); i++) {
current->next = new ListNode(nums[i]);
current = current->next;
}

return head;
}

// 从链表创建数组(用于输出)
vector<int> listToArray(ListNode* head)
{
vector<int> result;
ListNode* current = head;

while (current != nullptr) {
result.push_back(current->val);
current = current->next;
}

return result;
}

// 释放链表内存
void deleteList(ListNode* head)
{
ListNode* current = head;
while (current != nullptr) {
ListNode* next = current->next;
delete current;
current = next;
}
}

int main()
{
int n;
cin >> n;

vector<int> nums(n);
for (int i = 0; i < n; i++) {
cin >> nums[i];
}

// 创建链表
ListNode* head = createList(nums);

// 使用迭代方法反转链表
ListNode* reversedHead = reverseListIterative(head);

// 如果要使用递归方法,可以取消下面这行的注释,并注释掉上面这行
// ListNode* reversedHead = reverseListRecursive(head);

// 将反转后的链表转换为数组并输出
vector<int> result = listToArray(reversedHead);
for (int i = 0; i < result.size(); i++) {
cout << result[i];
if (i != result.size() - 1) {
cout << " ";
}
}
cout << endl;

// 释放内存
deleteList(reversedHead);

return 0;
}

时间复杂度

  • 迭代方法时间复杂度:O(n),其中 n 是链表的长度。我们只需要遍历链表一次。
  • 递归方法时间复杂度:O(n),其中 n 是链表的长度。递归调用的次数等于链表的长度。
  • 空间复杂度:O(1)(迭代方法)或 O(n)(递归方法,由递归调用栈占用的空间)。

代码解释

  • 反转链表是链表操作中的一个基础问题,可以用迭代或递归的方法解决。
  • 迭代方法的核心思想是维护三个指针:prev(前一个节点)、curr(当前节点)和nextTemp(下一个节点)。在遍历链表的过程中,我们将当前节点的next指针指向前一个节点,然后移动这三个指针,直到遍历完整个链表。
  • 递归方法的核心思想是先递归反转链表的剩余部分,然后反转当前节点和下一个节点的连接。递归的基本情况是空链表或只有一个节点的链表。
  • 在本题中,为了方便输入输出,我们使用数组来模拟链表的输入,并将反转后的链表转换为数组输出。在实际应用中,链表通常是直接给定的。