LeetCode 面试经典 150_链表_反转链表 II(60_92_C++_中等)
题目描述:
给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
输入输出样例:
示例 1:

输入 :head = [1,2,3,4,5], left = 2, right = 4
输出:[1,4,3,2,5]
示例 2:
输入 :head = [5], left = 1, right = 1
输出:[5]
提示:
链表中节点数目为 n
1 <= n <= 500
-500 <= Node.val <= 500
1 <= left <= right <= n
题解:
解题思路:
思路一(头插法):
1、创建一个 dummyHead(伪头节点),保存链表头。
- left 结点之前的结点无需翻转,首先定位到left位置的结点,并保存left前的一个结点信息为 pre,left结点信息为leftNode。
- 从left位置采用头插法插入pre结点之后。直至插入到right结点。
- 将right右侧结点连接到leftNode后
例: head = [1,2,3,4,5], left = 2, right = 4
1、定位left位置,leftNode=2,pre =1。dummyHead->1
2、从 2 开始采用头插法插入 1 之后,到right结束 ,dummyHead->1->4->3->2
3、将right右侧结点连接到leftNode后, dummyHead->1->4->3->2->5->nullptr
2、复杂度分析:
① 时间复杂度:O(n),n 代表链表中元素的个数,只遍历了一遍列表。
② 空间复杂度:O(1),使用常数个内存空间(只对源节点的next指向进行更改)。
代码实现
代码实现(思路一(头插法)):
cpp
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int left, int right) {
// 如果链表为空,或反转区间没有变化(left == right),直接返回原链表
if (!head || left == right){
return head;
}
// 创建虚拟头节点,简化处理,指向链表的头节点
ListNode *dummyHead = new ListNode(0, head);
ListNode *curNode = head; // 当前节点从链表的头开始
ListNode *pre = dummyHead; // pre指向虚拟头节点
// 1. 定位到反转区间的前一个节点(即位置 left-1)
for (int i = 1; i < left; i++) {
pre = pre->next; // pre移到left-1位置
curNode = curNode->next; // curNode移到left位置
}
// 2. 保存反转区域的起始节点
ListNode *leftNode = curNode;
ListNode *nextNode = nullptr; // 用于暂存当前节点的下一个节点
// 3. 反转从left到right的部分
for (int i = left; i <= right; i++) {
nextNode = curNode->next; // 记录当前节点的下一个节点
curNode->next = pre->next; // 当前节点指向pre->next
pre->next = curNode; // pre->next指向当前节点
curNode = nextNode; // 当前节点移到下一个节点
}
// 4. 连接反转后的部分与剩余部分
leftNode->next = curNode; // 将反转区域的尾节点连接到剩余部分
// 5. 返回新的链表头,虚拟头节点的next就是新链表的头
ListNode *ans = dummyHead->next;
// 删除虚拟头节点,避免内存泄漏
delete dummyHead;
return ans; // 返回新链表的头节点
}
};
以思路一为例进行调试
cpp
#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(0),next(next){}
};
//尾插法创建链表
ListNode *createList(vector<int> nums){
ListNode *head=nullptr,*tail=nullptr;
for (auto &num : nums){
if (head==nullptr){
head=tail=new ListNode(num);
}else{
tail->next=new ListNode(num);
tail=tail->next;
}
}
return head;
}
/** 头插法
* 首先定位到left位置的结点,并保存left前的一个结点信息为 pre,left结点信息为leftNode。
* 从left位置采用头插法插入pre结点之后。直至插入到right结点。
* 将right右侧结点连接到leftNode后
* 例子:head = [1,2,3,4,5], left = 2, right = 4
* 1、定位left位置,leftNode=2,pre =1。dummyHead->1
* 2、从 2 开始采用头插法插入 1 之后,到right结束 ,dummyHead->1->4->3->2
* 3、将right右侧结点连接到leftNode后, dummyHead->1->4->3->2->5->nullptr
*
*/
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int left, int right) {
// 如果链表为空,或反转区间没有变化(left == right),直接返回原链表
if (!head || left == right){
return head;
}
// 创建虚拟头节点,简化处理,指向链表的头节点
ListNode *dummyHead = new ListNode(0, head);
ListNode *curNode = head; // 当前节点从链表的头开始
ListNode *pre = dummyHead; // pre指向虚拟头节点
// 1. 定位到反转区间的前一个节点(即位置 left-1)
for (int i = 1; i < left; i++) {
pre = pre->next; // pre移到left-1位置
curNode = curNode->next; // curNode移到left位置
}
// 2. 保存反转区域的起始节点
ListNode *leftNode = curNode;
ListNode *nextNode = nullptr; // 用于暂存当前节点的下一个节点
// 3. 反转从left到right的部分
for (int i = left; i <= right; i++) {
nextNode = curNode->next; // 记录当前节点的下一个节点
curNode->next = pre->next; // 当前节点指向pre->next
pre->next = curNode; // pre->next指向当前节点
curNode = nextNode; // 当前节点移到下一个节点
}
// 4. 连接反转后的部分与剩余部分
leftNode->next = curNode; // 将反转区域的尾节点连接到剩余部分
// 5. 返回新的链表头,虚拟头节点的next就是新链表的头
ListNode *ans = dummyHead->next;
// 删除虚拟头节点,避免内存泄漏
delete dummyHead;
return ans; // 返回新链表的头节点
}
};
int main(int argc, char const *argv[])
{
vector<int> nums={1,2,3,4,5};
ListNode *head=createList(nums);
int left=2;
int right=4;
//验证二叉树是否创建成功
// while (head!=nullptr){
// cout<<head->val<<" ";
// head=head->next;
// }
Solution s;
ListNode *ans = s.reverseBetween(head,left,right);
while (ans!=nullptr){
cout<<ans->val<<" ";
ans=ans->next;
}
return 0;
}
LeetCode 面试经典 150_链表_反转链表 II(60_92)原题链接
欢迎大家和我沟通交流(✿◠‿◠)