利用编程思维做题之链表内指定区间反转

news2024/12/23 22:21:04

牛客网题目

 1. 理解问题

        给定一个单链表和两个整数 mn,要求反转链表中从位置 m 到位置 n 的节点,最后返回反转后的链表头节点。

示例:

  • 输入:链表 1 -> 2 -> 3 -> 4 -> 5 -> NULLm = 2n = 4

  • 输出:1 -> 4 -> 3 -> 2 -> 5 -> NULL

关键点:

  • 原地反转:要求空间复杂度为 O(1),可以有常量级别的中间值保存操作,。

  • 边界条件:需要考虑 m 为 1(即从链表头开始反转)以及 n 为链表长度的情况。

  • 指针处理:需要正确处理反转部分前后的节点连接。

2. 输入输出

  • 输入:

    • head:链表的头节点。

    • mn:指定反转的起始和结束位置,满足 1 ≤ m ≤ n ≤ 链表长度

  • 输出:

    • 反转指定区间后的链表头节点。

3. 链表结构

        链表节点的定义如下:

struct ListNode {
    int val;                // 节点的值
    struct ListNode *next;  // 指向下一个节点的指针
};

4. 制定策略

        为实现指定区间的反转,需要以下步骤:

  1. 定位到反转起始位置的前一个节点:

    • 使用一个辅助的 dummy 节点指向链表头,方便处理 m = 1 的情况。

    • 通过遍历,找到位置在 m - 1 的节点 prev

  2. 反转从位置 mn 的子链表:

    • 初始化指针:

      • start:指向位置 m 的节点,反转后的尾部节点,即反转部分的第一个应该是反转后的最后一个

      • then:指向位置 m + 1 的节点,反转操作中的当前节点

    • 反转过程:

      • then 节点逐个移动,并调整指针连接。

  3. 重新连接反转后的子链表:

    • 反转完成后,prevnext 指针指向新的子链表头节点(即反转部分),startnext 指针指向反转部分之后的节点

5. 实现代码

5.1 关键函数实现

struct ListNode* reverseBetween(struct ListNode* head, int m, int n) {
    if (head == NULL) return NULL;

    struct ListNode dummy;
    dummy.next = head;
    struct ListNode* prev = &dummy;

    // 移动 prev 到位置 m-1
    for (int i = 1; i < m; i++) {
        prev = prev->next;
    }

    struct ListNode* start = prev->next;
    struct ListNode* then = start->next;

    // 反转位置 m 到 n 的节点
    for (int i = 0; i < n - m; i++) {
        start->next = then->next; //要反转的第一个数值start往后移
        then->next = prev->next; //当前数值的后面是m-1的后面那个数
        prev->next = then; //m-1数值的后面是当前数值
        then = start->next; //当前数值往后移
    }

    return dummy.next;
}

假如m=2,n=4:

第一次循环(i = 0):

  dummy -> 1 -> 2 -> 3 -> 4 -> 5 -> NULL
                    ^    ^    ^
                  prev start then

第二次循环(i = 1):

 dummy -> 1 -> 4 -> 3 -> 2 -> 5 -> NULL
                   ^    ^    ^
                prev start then



5.2 完整的 C 语言代码

#include <stdio.h>
#include <stdlib.h>

// 定义链表节点
struct ListNode {
    int val;
    struct ListNode *next;
};

// 反转指定区间的函数
struct ListNode* reverseBetween(struct ListNode* head, int m, int n) {
    if (head == NULL) return NULL;

    struct ListNode dummy;
    dummy.next = head;
    struct ListNode* prev = &dummy;

    // 移动 prev 到位置 m-1
    for (int i = 1; i < m; i++) {
        prev = prev->next;
    }

    struct ListNode* start = prev->next;
    struct ListNode* then = start->next;

    // 反转指定区间
    for (int i = 0; i < n - m; i++) {
        start->next = then->next;
        then->next = prev->next;
        prev->next = then;
        then = start->next;
    }

    return dummy.next;
}

// 创建新节点的辅助函数
struct ListNode* createNode(int value) {
    struct ListNode* newNode = (struct ListNode*)malloc(sizeof(struct ListNode));
    if (!newNode) {
        perror("创建节点失败");
        exit(EXIT_FAILURE);
    }
    newNode->val = value;
    newNode->next = NULL;
    return newNode;
}

// 打印链表的辅助函数
void printList(struct ListNode* head) {
    struct ListNode* current = head;
    while (current != NULL) {
        printf("%d", current->val);
        if (current->next != NULL) {
            printf(" -> ");
        }
        current = current->next;
    }
    printf(" -> NULL\n");
}

// 释放链表内存
void freeList(struct ListNode* head) {
    struct ListNode* current = head;
    struct ListNode* temp;
    while (current != NULL) {
        temp = current->next;
        free(current);
        current = temp;
    }
}

// 测试代码
int main() {
    // 创建链表 {1 -> 2 -> 3 -> 4 -> 5}
    struct ListNode* 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);

    printf("原始链表:\n");
    printList(head);

    int m = 2, n = 4;
    struct ListNode* modifiedHead = reverseBetween(head, m, n);

    printf("反转第 %d 到 %d 个节点后的链表:\n", m, n);
    printList(modifiedHead);

    // 释放内存
    freeList(modifiedHead);

    return 0;
}
 

5.3 代码说明

  • reverseBetween 函数:

    • dummy 节点:创建一个哑节点 dummy,指向链表头 head,用于简化边界条件的处理。

    • prev 指针:初始化为 dummy,用于定位到反转区间的前一个节点。

    • start 和 then 指针:start 指向反转区间的第一个节点,then 指向需要反转的下一个节点。

    • 反转过程:通过调整指针的指向,逐步将 then 节点插入到 prev 后面,实现局部反转。

  • 辅助函数:

    • createNode:创建新节点,初始化节点的值和指针。

    • printList:遍历并打印链表的所有节点值。

    • freeList:释放链表占用的内存,防止内存泄漏。

  • 主函数 main:

    • 创建示例链表并输出。

    • 调用 reverseBetween 函数反转指定区间的节点。

    • 输出反转后的链表结果。

    • 释放链表内存。

5.4 运行结果

        原始链表:
        1 -> 2 -> 3 -> 4 -> 5 -> NULL
        反转第 2 到 4 个节点后的链表:
        1 -> 4 -> 3 -> 2 -> 5 -> NULL

6. 时间和空间复杂度分析

  • 时间复杂度:O(n)

    • 需要遍历链表一次,找到位置 m,然后进行 n - m 次反转操作。
  • 空间复杂度:O(1)

    • 只使用了常量级的额外空间,主要是几个指针变量。

7. 总结

        通过上述方法,我们成功实现了对单链表指定区间的节点进行反转的操作。关键在于正确定位需要反转的区间,并在反转过程中维护好前后节点的连接关系。

        此方法不仅有效解决了本题,还可以应用于其他涉及链表部分反转或节点调整的题目。熟练掌握链表操作和指针的使用,对于处理类似问题至关重要。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2205933.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

《市场营销学》PPT课件.ppt

网盘&#xff1a;https://pan.notestore.cn/s.html?id29https://pan.notestore.cn/s.html?id29

山西农业大学20241011

03-JAVASCRIPT 一.数组二.BOM1. window对象2. location对象3. history对象4. navigator对象5. screen对象6. cookie对象 三.DOM操作1. 概述2. 查找元素2.1 id方式2.2 标签名方式2.3 class名方式2.4 css选择器方式 一.数组 <script>// 1. 创建数组, 通过数组字面量// …

不卷且创新idea:KAN+特征提取!10篇高分套路拆解,快来抄作业!

今天和大家分享一种创新的深度学习技术&#xff1a;KAN特征提取。 这种技术通过引入KAN来增强模型的特征处理能力&#xff0c;借由KAN的自适应激活函数&#xff0c;动态调整数据特性&#xff0c;从而有效提取更加准确的特征&#xff0c;实现更高性能的模型表现。 这种优势让K…

离散微分几何基础:流形概念与网格数据结构

一、流形概念的引入 &#xff08;一&#xff09;微分几何核心概念——流形 在微分几何的广袤领域中&#xff0c;流形概念占据着核心地位。它如同一个神秘的基石&#xff0c;支撑着我们对各种几何形状和空间的深入理解。就像网格和抽象的单纯复数是我们探索拓扑结构&#xff08…

使用阿里云盘将服务器上的文件上传/下载到云盘/服务器

阿里云盘官方文档&#xff1a; 具体的操作步骤这里都有&#xff1a; https://github.com/tickstep/aliyunpan 具体步骤 &#xff1a; 安装&#xff1a; wget https://github.com/tickstep/aliyunpan/releases/download/v0.3.4/aliyunpan-v0.3.4-linux-amd64.zip【这里最好下…

服务器与内存市场|2025预测动态早知道

根据TrendForce的数据分析报告&#xff0c;三大DRAM供应商在2023年服务器总bit增长率经历了不同程度下滑后&#xff0c;2024年市场迎来了反弹&#xff0c;增长率分别达到了9.9%/12.3%/24.1%。这一转变表明服务器DRAM在三大供应商中的比例预计将会增加。与此同时&#xff0c;由于…

Java项目实战II养老||基于Java+Spring Boot+MySQL的社区智慧养老监护管理平台设计与实现(源码+数据库+文档)

目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 随着老龄化…

ConditionVideo: 无训练的条件引导视频生成 | AAAI 2024

作者&#xff1a;彭博&#xff0c;上海人工智能实验室与上海交大2023级联培博士。 最近的工作已经成功地将大规模文本到图像模型扩展到视频领域&#xff0c;产生了令人印象深刻的结果&#xff0c;但计算成本高&#xff0c;需要大量的视频数据。在这项工作中&#xff0c;我们介…

26.第二阶段x86游戏实战2-C++遍历背包物品

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 本次游戏没法给 内容参考于&#xff1a;微尘网络安全 本人写的内容纯属胡编乱造&#xff0c;全都是合成造假&#xff0c;仅仅只是为了娱乐&#xff0c;请不要…

如何创建一个node.js项目并配置

要想创建一个node.js项目&#xff0c;首先的话要先有npm和node.js环境&#xff0c;没有的同学可以参考一下连接&#xff1a;npm安装-详细教程-CSDN博客 一、创建node.js项目文件 注意&#xff1a;windows系统创建时文件名不能有汉字和空格 我创建了一个文件夹叫nodejswork 二…

离线使用k8s部署项目

docker的安装与完全卸载&#xff08;亲测可用&#xff09; docker的安装与完全卸载 然后配置镜像加速器 vi /etc/docker/daemon.json 将找到的镜像仓库地址写入 具体内容可以参考此网站时刻更新镜像源仓库 然后保存退出 执行 systemctl daemon-reloadsystemctl restart…

第J3周:DenseNet算法实战与解析

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 文章目录 一、前期工作二、模型复现1、设置GPU2、导入数据3、加载数据4. 配置数据集5. 可视化数据6、构建DenseNet121网络7、编译8、训练模型9、模型评估 三、…

H3C IPsec over GRE VPN 实验

H3C IPsec over GRE VPN 实验 实验拓扑 ​​ 实验需求 按照图示配置 IP 地址,R1 和 R3 配置 Loopback0 口模拟业务网段R1 和 R3 上配置默认路由连通公网R1 和 R3 上配置 IPsec over GRE VPN 来连通两端内网R1 和 R3 配置 OSPF 来传递内网路由实验步骤 按照图示配置 IP 地址…

b站视频下载, b站视频下载助手 如何下载哔哩哔哩视频

1. 链接&#xff1a; 哔哩哔哩(bilibili)视频解析下载 - 保存B站视频到手机、电脑 2. 下载即可

多线程——线程安全

目录 前言 一、观察线程不安全 二、线程安全概念 三、产生线程安全问题的原因 1.分析示例代码 2.线程随机调度 3.修改共享数据 4.原子性 5.可见性 6.指令重排序 四、解决示例代码的问题 结尾 前言 我们学习多线程编程的目的是为了能够实现“并发编程”&#xff0c;…

WAFER连接器在现代电子领域的多样化应用

WAFER连接器是一种广泛应用于现代电子设备中的连接组件&#xff0c;其设计和功能使其在多种应用场景中表现出色。作为一种高效、可靠的连接解决方案&#xff0c;WAFER连接器凭借其小巧、精密的设计赢得了工程师和设计师的青睐。这篇文章将探讨WAFER连接器在不同行业和应用领域中…

力扣第1题:两数之和(图解版)

Golang版本 func twoSum(nums []int, target int) []int {m : make(map[int]int)for i : range nums {if _, ok : m[target - nums[i]]; ok {return []int{i, m[target - nums[i]]}} m[nums[i]] i}return nil }

pip install ERROR: Could not install packages due to an OSError

问题解决 pip install xxx报错&#xff1a; WARNING: Retrying (Retry(total4, connectNone, readNone, redirectNone, statusNone)) ERROR: Could not install packages due to an OSError 使用 pip install xxx --user 安装

游离的 HEAD 如何解决

简介 问题描述&#xff1a;使用 IDEA 在提交代码时&#xff0c;禁止提交 如何解决&#xff1a;迁出分支再提交&#xff0c;最后合并到 main 或 master 上 如何解决

面向抽象和面向接口的区别

‌1.概念 01、抽象类 在 Java 中&#xff0c;通过关键字 abstract 定义的类叫做抽象类。Java 是一门面向对象的语言&#xff0c;因此所有的对象都是通过类来描述的&#xff1b;但反过来&#xff0c;并不是所有的类都是用来描述对象的&#xff0c;抽象类就是其中的一种。 以下示…