LeetCode 面试经典 150_链表_反转链表 II(60_92_C++_中等)(头插法)

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)原题链接

欢迎大家和我沟通交流(✿◠‿◠)

相关推荐
2301_803554523 小时前
socket编程
c++
热爱编程的OP3 小时前
Linux进程池与管道通信详解:从原理到实现
linux·开发语言·c++
许强0xq4 小时前
Q6: 如何计算以太坊交易的美元成本?
面试·web3·区块链·智能合约·dapp
晚风吹长发10 小时前
二分查找算法+题目详解
c++·算法·二分查找
pilgrim5311 小时前
结合 Leetcode 题探究KMP算法
算法·leetcode
罗义凯11 小时前
其中包含了三种排序算法的注释版本(冒泡排序、选择排序、插入排序),但当前只实现了数组的输入和输出功能。
数据结构·c++·算法
狂炫冰美式12 小时前
前端实时推送 & WebSocket 面试题(2026版)
前端·http·面试
春蕾夏荷_72829772512 小时前
c++ easylogging 使用示例
c++·log·easylogging
syt_biancheng12 小时前
Day3算法训练(简写单词,dd爱框框,3-除2!)
开发语言·c++·算法·贪心算法
自然数e13 小时前
C++多线程【线程管控】之线程转移以及线程数量和ID
开发语言·c++·算法·多线程