【LeetCode_206】反转链表

【LeetCode_206】反转链表

刷爆LeetCode系列

LeetCode第206题:反转链表

github地址

有梦想的电信狗

前言

本文用C++实现LeetCode206题:反转链表


题目描述

在这里插入图片描述
在这里插入图片描述

题目与思路分析

目标分析

  1. 有单链表的头节点 head ,反转原链表
  2. 返回反转后的链表的头指针
  3. 提高要求:时间复杂度为O(n),空间复杂度为O(1)

思路一:反转链表的指针指向

思路:遍历一遍链表,将当前结点的next指针,指向其前驱节点,即可实现链表的反转。最终返回链表的最后一个节点即可(原链表的最后一个结点作为新链表的头结点

操作

  • head == nullptr时,为空链表,直接return nullptr;
  • 遍历链表
    • curNode:从头结点head开始,依次遍历链表,更改指针的指向
      • 当前结点的next指针,指向其前驱节点,因此需要提前保存当前结点的前驱节点
    • curPrev:记录curNode的前驱节点,方便反转指针指向,初始值为nullptr
    • curNext:提前保存curNode的后继结点,防止反转指针指向curNode无法移动到下一个结点
  • 更改指向
    • 保存curNode的下一个结点ListNode* curNext = curNode->next
    • 更改当前结点的next指针curNode->next = curPrev
    • curPrev和curNode依次向后移动
      • curPrev = curNode;
      • curNode = curNext;
  • 最终return curPrevcurPrev最后的位置就是链表的尾结点
在 while 循环内保存 curNext,保证了 curNode 一定不为空,避免了对空指针解引用可以保证一定可以取到 curNext,为空或非空
在这里插入图片描述
  • 链表只有一个结点的情况
在这里插入图片描述

思路二:取链表的结点,头插到新链表中

思路:创建一个新链表,头结点为newHead,初始为nullptr。遍历原链表,将原链表中的节点依次头插到新链表中,头插后更新newHead,即可实现链表的反转,最终返回newHead

操作

  • 遍历链表
    • curNode:从头结点head开始,依次遍历链表,进行头插操作。将当前结点的next指针,指向新链表的newHead,再更新newHead的值。之后curNode移向下一个结点。由于头插后curNode->next的值更改,不能通过curNode = curNode->next的方式移动,因此需要提前保存curNode的后继节点
    • curNext:提前保存curNode的后继结点,防止头插curNode无法移动到下一个结点
  • 头插
    • 保存curNode的下一个结点ListNode* curNext = curNode->next
    • 更改当前结点的next指针curNode->next = newHead
    • 更新newHead的值newHead = curNode;
    • curNode向后移动curNode = curNext;
  • 最终return newHeadnewHead即为新链表的头结点
在 while 循环内保存 curNext,保证了 curNode 一定不为空,避免了对空指针解引用可以保证一定可以取到 curNext,为空或非空
在这里插入图片描述

代码实现

思路一:反转指针指向

以下两种写法是保存curNext指针的方式不同

  • 在while循环外保存curNext指针,初始值可能为nullptr
classSolution{public: ListNode*reverseList(ListNode* head){// 空链表的情况if(head ==nullptr)returnnullptr; ListNode* curNode = head; ListNode* curPrev =nullptr; ListNode* curNext = head->next;// 链表只有一个结点的情况if(curNext ==NULL)return head;while(curNode){ curNode->next = curPrev; curPrev = curNode; curNode = curNext;// curNext可能为空,需要判断 非空时再向后移动if(curNext) curNext = curNext->next;}return curPrev;}};
  • 在while循环外保存curNext指针:不涉及对curNext的解引用,即使为空也不影响
// 思路一、反转指针classSolution{public: ListNode*reverseList(ListNode* head){if(head ==nullptr)returnnullptr; ListNode* curNode = head; ListNode* curPrev =nullptr;while(curNode){// 在 while 循环内保存 curNext,保证了 curNode 一定不为空,避免了对空指针解引用// 可以保证一定可以取到 curNext ,为空或非空 ListNode* curNext = curNode->next; curNode->next = curPrev; curPrev = curNode; curNode = curNext;}return curPrev;}};

思路二:取原链表中的节点,头插到新链表

  • 取链表中的节点,头插到新链表
// 思路二、取链表中的节点,头插到新链表classSolution{public: ListNode*reverseList(ListNode* head){ ListNode* newHead =nullptr,*curNode = head;while(curNode){// 提前保存 curNode 的下一个结点 ListNode* curNext = curNode->next;// 头插 curNode->next = newHead; newHead = curNode;// curNode 向后移动 curNode = curNext;}return newHead;}};

试错代码

  • 初次尝试时的错误代码:
  • 错误原因:
    • 第一次头插后,链表直接断开了,curNode无法移动到下一个结点
// 思路二 取结点,头插到新链表中classSolution{public: ListNode*reverseList(ListNode* head){ ListNode* newHead =nullptr; ListNode* curNode = head;while(curNode){// 错误原因,第一次头插后,链表直接断开了,curNode无法移动到下一个结点if(newHead ==nullptr){ newHead = curNode; newHead->next =nullptr;}else{ curNode->next = newHead; newHead = curNode;} curNode = curNode->next;}return newHead;}};
  • 纠错后的代码
// 思路二 取结点,头插到新链表中classSolution{public: ListNode*reverseList(ListNode* head){ ListNode* newHead =nullptr; ListNode* curNode = head;while(curNode){ ListNode* curNext = curNode->next;// 提前保存 下一个结点if(newHead ==nullptr){ newHead = curNode; newHead->next =nullptr;}else{ curNode->next = newHead; newHead = curNode;} curNode = curNext;}return newHead;}};

算法代码优化

思路一优化:

  • 优化点:无需单独判断链表为空时的情况
    • 链表为空时不进入while循环,直接返回 curPrev,而 curPrev 初始值为 nullptr
// 思路一、反转指针classSolution{public: ListNode*reverseList(ListNode* head){ ListNode* curNode = head,*curPrev =nullptr;while(curNode){// 在 while 循环内保存 curNext,保证了 curNode 一定不为空,避免了对空指针解引用// 可以保证一定可以取到 curNext ,为空或非空 ListNode* curNext = curNode->next; curNode->next = curPrev; curPrev = curNode; curNode = curNext;}return curPrev;}};
  • 思路二已经足够简洁正确,无需优化

以上就是本文的所有内容了,如果觉得文章对你有帮助,欢迎 点赞⭐收藏 支持!如有疑问或建议,请在评论区留言交流,我们一起进步

分享到此结束啦
一键三连,好运连连!
你的每一次互动,都是对作者最大的鼓励!征程尚未结束,让我们在广阔的世界里继续前行! 🚀

Read more

如何在VS Code中安装GitHub Copilot进行AI编程

如何在VS Code中安装GitHub Copilot进行AI编程

本文教您轻松在VS Code中玩转GitHub Copilot:从安装认证到实战网页开发,5分钟解锁AI编程神器,还能自由切换模型、实时调试代码! 在Visual Studio Code中搭建GitHub Copilot编程环境需要经过几个关键步骤,以下是详细指南: 环境准备阶段 1. 安装最新版VS Code(当前版本≥1.85)官网下载地址:https://code.visualstudio.com/ 2. 拥有有效的GitHub账户(建议启用双重验证) 注册地址:https://github.com/ 3. 稳定的网络连接(Copilot需实时云端交互) 安装流程 1、安装VS Code后,选择”Use All features with Copilot for free”。如果已经安装VS Code,可以打开VS Code扩展市场(

By Ne0inhk
开源智能体搭建平台MaxKB4j 技术文档

开源智能体搭建平台MaxKB4j 技术文档

MaxKB4j 技术文档 项目概述 MaxKB4j (Max Knowledge Base for Java) 是一个基于 Java/Spring Boot 和 LangChain4j 构建的开源的 RAG(检索增强生成)知识库和 LLM 工作流平台,支持多模型集成、可视化工作流编排、知识库问答和多模态能力,专为构建企业级智能问答系统而设计。 核心特性 * 开箱即用的知识库问答: 支持上传本地文档或自动抓取网页内容,自动完成文本分块 → 向量化 → 向量数据库存储 → RAG 流程构建 * 模型无关的灵活集成: 支持多种主流大语言模型(OpenAI、Claude、Gemini、DeepSeek、Qwen、Ollama 等) * 可视化工作流编排: 内置低代码 AI 工作流引擎,支持条件分支、函数调用、多轮对话记忆 * MCP

By Ne0inhk
VLM Unlearning 有关论文阅读总结与梳理

VLM Unlearning 有关论文阅读总结与梳理

文章目录 目录 前言 一、什么是 Unlearning 二、AUVIC 三、Neuron Pruning 四、 Neuron Path Editing 五、 MLLM Eraser 前言 本文整理了当前多模态大模型(VLM)中常见的 Unlearning 技术路线,主要包括: * AUVIC * Neuron Pruning * Neuron Path Editing * MLLM Eraser 这些方法的核心目标都是: 让模型“遗忘”指定知识,同时尽量不影响其它知识。 一、什么是 Unlearning 在多模态大模型(Vision-Language Model / VLA)中,我们经常需要: * 删除隐私数据 * 移除不安全知识 * 删除特定人物或敏感概念

By Ne0inhk
Flutter for OpenHarmony: Flutter 三方库 github 在鸿蒙应用中深度集成 GitHub API 构建高效的开发者协作工具(出海与工具链开发)

Flutter for OpenHarmony: Flutter 三方库 github 在鸿蒙应用中深度集成 GitHub API 构建高效的开发者协作工具(出海与工具链开发)

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net 前言 在进行 OpenHarmony 为核心的开发者工具、代码托管助手或出海类社交应用开发时,与 GitHub 的数据打交道是必修课。我们需要实现: 1. 鸿蒙端展示用户的 GitHub 仓库列表和 Stars。 2. 自动化管理 Issues,或是监控 Repo 的 PR 动态。 3. 利用 GitHub 账号直接登录鸿蒙端的开发者社区。 github 软件包是 Flutter 生态中功能最全、维护最稳健的 GitHub REST API 封装库。它提供了 100% 强类型的接口定义,让你的鸿蒙应用能以最轻量化的方式接入全球最大的开源社区数据。 一、API 通讯与对象模型 github 库将繁杂的 JSON

By Ne0inhk