c/c++蓝桥杯经典编程题100道(16)链表反转

devtools/2025/2/10 21:36:38/

链表反转

c/c++蓝桥杯经典编程题100道-目录-CSDN博客


目录

链表反转

一、题型解释

二、例题问题描述

三、C语言实现

解法1:迭代反转(难度★)

解法2:递归反转(难度★★)

解法3:分组反转(难度★★★)

四、C++实现

解法1:迭代反转(难度★)

解法2:使用STL容器辅助(难度★★)

五、总结对比表

六、特殊方法与内置函数补充

1. C++智能指针

2. 链表调试技巧


一、题型解释

链表反转是将链表节点的连接方向完全逆序的操作。常见题型:

  1. 基础反转:反转整个单链表(如 1→2→3→4→5 转为 5→4→3→2→1)。

  2. 部分反转:反转链表的某个区间(如反转第2到第4个节点)。

  3. 分组反转:每k个节点为一组进行反转(如k=2时,1→2→3→4→5 转为 2→1→4→3→5)。

  4. 递归反转:用递归思想实现链表反转。


二、例题问题描述

例题1:输入链表 1→2→3→4→5,输出反转后的链表 5→4→3→2→1
例题2:输入链表 1→2→3→4→5 和区间 [2,4],输出 1→4→3→2→5
例题3:输入链表 1→2→3→4→5 和k=2,输出 2→1→4→3→5
例题4:输入链表 1→2,输出 2→1


三、C语言实现

解法1:迭代反转(难度★)

通俗解释

  • 像翻书一样,逐个节点改变指针方向,将当前节点的next指向前一个节点。

c

#include <stdio.h>
#include <stdlib.h>struct ListNode {int val;struct ListNode *next;
};// 迭代反转链表
struct ListNode* reverseList(struct ListNode* head) {struct ListNode *prev = NULL, *curr = head;while (curr != NULL) {struct ListNode *nextTemp = curr->next; // 保存下一个节点curr->next = prev; // 当前节点指向前一个节点prev = curr;       // 前一个节点后移curr = nextTemp;   // 当前节点后移}return prev; // prev最终指向新链表的头节点
}// 打印链表
void printList(struct ListNode* head) {while (head != NULL) {printf("%d→", head->val);head = head->next;}printf("NULL\n");
}int main() {// 构建链表 1→2→3→4→5struct ListNode node5 = {5, NULL};struct ListNode node4 = {4, &node5};struct ListNode node3 = {3, &node4};struct ListNode node2 = {2, &node3};struct ListNode node1 = {1, &node2};struct ListNode* newHead = reverseList(&node1);printList(newHead); // 输出 5→4→3→2→1→NULLreturn 0;
}

代码逻辑

  1. 初始化指针prev(前一个节点)初始为NULL,curr(当前节点)初始为头节点。

  2. 循环操作

    • 保存下一个节点 nextTemp = curr->next

    • 将当前节点的 next 指向 prev

    • prev 和 curr 向后移动。

  3. 终止条件:当 curr 为NULL时,prev 成为新链表的头节点。


解法2:递归反转(难度★★)

通俗解释

  • 假设后面的链表已经反转,只需处理当前节点和已反转部分的连接。

c

struct ListNode* reverseListRecursive(struct ListNode* head) {if (head == NULL || head->next == NULL) {return head; // 基线条件:空链表或只剩一个节点}struct ListNode* newHead = reverseListRecursive(head->next); // 递归反转后续链表head->next->next = head; // 当前节点的下一个节点指向自己(反转方向)head->next = NULL;       // 断开原有连接return newHead; // 返回新头节点
}int main() {// 测试递归反转struct ListNode* newHead = reverseListRecursive(&node1);printList(newHead); // 输出 5→4→3→2→1→NULLreturn 0;
}

代码逻辑

  1. 基线条件:链表为空或只剩一个节点时直接返回。

  2. 递归调用:先反转 head->next 之后的链表,得到新头节点 newHead

  3. 反转当前节点:将当前节点的下一个节点的 next 指向自己,再断开自己的 next

  4. 递归栈展开:从最后一个节点开始逐层反转。


解法3:分组反转(难度★★★)

通俗解释

  • 每k个节点为一组,组内反转,若剩余节点不足k个则保持原顺序。

c

struct ListNode* reverseKGroup(struct ListNode* head, int k) {struct ListNode *curr = head;int count = 0;// 检查是否足够k个节点while (curr != NULL && count < k) {curr = curr->next;count++;}if (count == k) { // 足够k个节点时反转struct ListNode *prev = reverseKGroup(curr, k); // 递归处理后续分组// 反转当前k个节点while (count-- > 0) {struct ListNode *nextTemp = head->next;head->next = prev;prev = head;head = nextTemp;}head = prev;}return head; // 不足k个时直接返回原头节点
}int main() {// 测试分组反转(k=2)struct ListNode* newHead = reverseKGroup(&node1, 2);printList(newHead); // 输出 2→1→4→3→5→NULLreturn 0;
}

代码逻辑

  1. 检查分组长度:遍历链表,判断剩余节点是否足够k个。

  2. 递归处理后续分组:若足够,递归反转后续链表,返回已反转部分的头节点。

  3. 反转当前分组:用迭代法反转当前k个节点,连接到已反转部分。

  4. 终止条件:若不足k个,直接返回当前头节点。


四、C++实现

解法1:迭代反转(难度★)

通俗解释

  • 与C语言迭代法逻辑相同,使用指针操作。

cpp

#include <iostream>
using namespace std;struct ListNode {int val;ListNode *next;ListNode(int x) : val(x), next(NULL) {}
};class Solution {
public:ListNode* reverseList(ListNode* head) {ListNode *prev = NULL, *curr = head;while (curr != NULL) {ListNode *nextTemp = curr->next;curr->next = prev;prev = curr;curr = nextTemp;}return prev;}
};int main() {ListNode *head = new ListNode(1);head->next = new ListNode(2);head->next->next = new ListNode(3);head->next->next->next = new ListNode(4);head->next->next->next->next = new ListNode(5);Solution sol;ListNode *newHead = sol.reverseList(head);while (newHead != NULL) {cout << newHead->val << "→";newHead = newHead->next;}cout << "NULL" << endl; // 输出 5→4→3→2→1→NULLreturn 0;
}

代码逻辑

  • 与C语言版本完全一致,但使用C++的类和对象封装。


解法2:使用STL容器辅助(难度★★)

通俗解释

  • 将链表存入容器(如栈或数组),反向构建新链表。

cpp

ListNode* reverseListSTL(ListNode* head) {vector<int> values;ListNode *curr = head;while (curr != NULL) { // 存入数组values.push_back(curr->val);curr = curr->next;}ListNode *dummy = new ListNode(0);curr = dummy;for (int i = values.size() - 1; i >= 0; i--) { // 反向遍历数组curr->next = new ListNode(values[i]);curr = curr->next;}return dummy->next;
}int main() {ListNode *head = new ListNode(1);head->next = new ListNode(2);head->next->next = new ListNode(3);ListNode *newHead = reverseListSTL(head);// 输出 3→2→1→NULLreturn 0;
}

代码逻辑

  1. 存储节点值:遍历链表,将节点的值存入数组。

  2. 反向构建链表:从数组末尾开始遍历,创建新节点并连接。

  3. 优点:代码简单,但空间复杂度为O(n)。


五、总结对比表

方法时间复杂度空间复杂度优点缺点
迭代反转O(n)O(1)高效,无需额外空间需处理多个指针
递归反转O(n)O(n)代码简洁栈空间消耗大
分组反转O(n)O(1)支持复杂需求实现较复杂
STL容器辅助O(n)O(n)代码简单空间效率低

六、特殊方法与内置函数补充

1. C++智能指针

  • 作用:自动管理内存,避免内存泄漏(需包含 <memory> 头文件)。

  • 示例

    shared_ptr<ListNode> node = make_shared<ListNode>(1);

2. 链表调试技巧

  • 打印链表:编写打印函数时,可在每个节点后添加箭头(),末尾标记 NULL

  • 图形化工具:使用在线数据结构可视化工具(如VisuAlgo)辅助调试。

c/c++蓝桥杯经典编程题100道-目录-CSDN博客


http://www.ppmy.cn/devtools/157740.html

相关文章

Excel大数据量导入导出

github源码 地址&#xff08;更详细&#xff09; : https://github.com/alibaba/easyexcel 文档&#xff1a;读Excel&#xff08;文档已经迁移&#xff09; B 站视频 : https://www.bilibili.com/video/BV1Ff4y1U7Qc 一、JAVA解析EXCEL工具EasyExcel Java解析、生成Excel比较…

可以在个人电脑上部署的主流开源大模型

目前主流开源的大模型发展迅速,许多模型经过优化后可以在个人电脑(甚至CPU或消费级GPU)上运行。以下是当前主流的开源大模型及其在个人设备上的部署可行性总结: 一、主流开源大模型 1.DeepSeek系列 DeepSeek大语言模型算法:以Transformer架构为基础,自主研发的深度神经网…

问卷数据分析|SPSS之分类变量描述性统计

1.点击分析--描述统计--频率 2. 选中分类变量&#xff0c;点击中间箭头 3.图表选中条形图&#xff0c;图表值选择百分比&#xff0c;选择确定 4.这里显示出了描述性统计的结果 5.下面就是图形&#xff0c;但SPSS画的图形都不是很好啊看&#xff0c;建议用其他软件画图&#xff…

Bash (Bourne-Again Shell)、Zsh (Z Shell)

文章目录 1. 历史背景2. 主要区别3. 功能对比自动补全插件和主题路径扩展提示符定制 4. 性能5. 使用场景6. 如何切换 Shell7. 总结 以下是 Bash 和 Zsh 之间的主要区别&#xff0c;列成表格方便对比&#xff1a; 特性BashZsh默认Shell大多数Linux发行版默认ShellmacOS默认She…

通过docker安装部署deepseek以及python实现

前提条件 Docker 安装:确保你的系统已经安装并正确配置了 Docker。可以通过运行 docker --version 来验证 Docker 是否安装成功。 网络环境:保证设备有稳定的网络连接,以便拉取 Docker 镜像和模型文件。 步骤一:拉取 Ollama Docker 镜像 Ollama 可以帮助我们更方便地管理…

Android开发签名校验

Android开发签名校验 有一些平台需要我们做签名校验才能通过审核&#xff0c;其实做Android签名校验也不是很难 直接上代码&#xff1a; class SignCheck {/*** 获取应用的签名*/private fun getCer(mContext: Context): String? {var packageInfo: PackageInfo? nulltry…

[权限提升] Linux 提权 维持 — 系统错误配置提权 - 通配符(ws)注入提权

关注这个专栏的其他相关笔记&#xff1a;[内网安全] 内网渗透 - 学习手册-CSDN博客 0x01&#xff1a;通配符&#xff08;ws&#xff09;注入提权原理 通配符注入提权的核心是利用通配符的扩展特性&#xff0c;在命令执行时生成意外的参数或文件名&#xff0c;从而改变命令的行…

DeepSeek Janus Pro 论文解析

目录 介绍 统一的多模态理解与生成 图像理解任务 图像生成任务 统一模型的好处 Janus 和 Janus Pro 架构 Janus Pro主要设计原理 Janus Pro 图像编码器 LLM 处理和输出 Rectified Flow Janus Pro 训练流程 第一阶段——适应 第二阶段——统一预训练 第三阶段——监…