扬帆数据结构算法之雅舟航程,漫步C++幽谷——LeetCode刷题之移除链表元素、反转链表、找中间节点、合并有序链表、链表的回文结构

扬帆数据结构算法之雅舟航程,漫步C++幽谷——LeetCode刷题之移除链表元素、反转链表、找中间节点、合并有序链表、链表的回文结构
在这里插入图片描述
在这里插入图片描述
人无完人,持之以恒,方能见真我!!!
共同进步!!

文章目录

一、移除链表元素

题目链接:移除链表元素
我们先来看看题目的描述
在这里插入图片描述


在这里插入图片描述
根据题目描述我们就可以大致明白题意,就是将一个链表中的某个值的节点删除,然后返回新链表的头结点,然后题目要我们实现的函数给了我们头结点,以及要删除的数据,我们要把相应的节点删除

思路一

首先最简单的思路就是,我们可以通过之前实现的链表的方法用上,首先使用Find方法找到对应的值,然后使用Erase方法删除,直到Find方法返回空指针结束
由于这个方法思路比较好实现,这里就不再赘述了,可以自己尝试一下,我们的关键是更优方法的思路二

思路二

这个题其实跟我们在刷顺序表题的时候遇见类似的,只不过之前要删除的是数组中的元素,这道题是删除链表节点,不过本质上是相同的,上次我们使用了双指针,这次我们还是可以使用双指针
具体思路也很像之前的那个题,题目让返回新链表的头结点,没有说必须是原链表的头结点,所以我们可以新建一个链表,如果遍历到原链表中节点的值不是题目给定的值,也就是不是我们要删除的节点,那么我们就把它尾插到新链表
我们要注意的是,如果遇到了要插入的节点,但是新链表的头为空,我们就要让新链表的头和尾都指向这个节点,其它情况就正常尾插
还有一个重要的地方就是,当我们把链表移动完毕之后,新链表的尾结点可能还指向原链表的节点,我们要把它置为空,题解如下:
typedefstructListNode ListNode;structListNode*removeElements(structListNode* head,int val){ ListNode* newhead,*newtail;//定义新链表的头尾节点 newhead = newtail =NULL;//首尾都指向自己 ListNode* pcur = head;//定义新链表的当前节点while(pcur){if(pcur->val != val){//这里是首节点的判断节点if(newhead ==NULL){ newhead = newtail = pcur;}else{ newtail->next = pcur; newtail = pcur;}} pcur = pcur->next;//继续向后走}if(newtail) newtail->next =NULL;//如果尾结点不为空,就置空尾结点的nextreturn newhead;//返回新的头结点}
在这里插入图片描述

二、合并两个有序链表

题目链接:合并两个有序链表
在这里插入图片描述


在这里插入图片描述
题目给我们两个有序链表,要求我们把这两个链表合并成一个新的有序链表,然后返回它的头结点

思路:

这个问题是不是有点似曾相识,跟我们之前的合并有序数组是一样的,我们当时的方法就是使用双指针,只是合并有序数组时是要求我们在第一个数组中进行修改,不能新建一个数组返回
但是这道题还要简单一些,它可以新建一个链表,我们可以通过双指针遍历要合并的链表,比较两个链表中节点的大小,谁小谁就尾插到新链表,直到有一个链表走到空就停止循环
但是我们要注意的一点是,虽然有一个链表走到空了,也就是一个链表中的节点都插入到新链表了,但是另一个链表可能还有节点,所以我们要判断一下,如果两个链表中还有一个链表不为空,那么直接将它的所有节点尾插到新链表
还有就是做一个特殊处理,因为两个链表中可能有空链表,上面的方法就跑不通,所以我们判断一下,如果有一个链表为空,那么直接返回另一个链表,题解如下:
structListNode*mergeTwoLists(structListNode* list1,structListNode* list2){//首先对特殊情况进行处理if(list1 ==NULL){return list2;}if(list2 ==NULL){return list1;}//赋值头结点structListNode* pcur1 = list1,*pcur2 = list2;//记录头结点和尾结点structListNode* newhead =NULL,*newtail =NULL;//进入循环开始判断while(pcur1 && pcur2){if(pcur1->val < pcur2->val){if(newhead ==NULL)//首节点是空说明没有节点{ newhead = newtail = pcur1;}else{ newtail->next = pcur1; newtail = pcur1;//赋值新的尾结点} pcur1 = pcur1->next;}else{if(newhead ==NULL)//首节点是空说明没有节点{ newhead = newtail = pcur2;}else{ newtail->next = pcur2; newtail = pcur2;//赋值新的尾结点} pcur2 = pcur2->next;}}//走出循环再对两个链表进行判空if(pcur1){ newtail->next = pcur1;}if(pcur2){ newtail->next = pcur2;}return newhead;}
在这里插入图片描述

优化:

上面的代码是一种题解,但是我们可以发现,这个代码写起来有点麻烦,有一些重复的动作,就是在每次插入之前,我们要判断链表是否为空,如果为空要让新链表的头和尾都指向要插入的节点
那我们能不能让代码更加简洁一点呢?就是不必每次插入节点前都判断链表是否为空,这里就可以用上我们之前学过的带头的概念,我们申请一个不保存数据的哨兵位当作链表默认的头
这样我们的新链表默认就有了一个节点,不为空了,可以直接在哨兵位后面尾插节点,不用判断链表是否为空,最后返回时就返回哨兵位的下一个节点就可以了,它就是我们新链表中保存数据的头节点
不过由于我们的哨兵位是通过malloc来的,所以最后代码结束时不要记得把它释放掉,以免造成内存泄漏,如下:
上面的代码是一种题解,但是啊我们可以发现这个代码写起来还是有一些麻烦的,多了一些重复的动作,就是在每次尾插链表前,我们都要判断链表是否为空,如果为空就要让新链表的头和尾都要指向要插入的节点
structListNode*mergeTwoLists(structListNode* list1,structListNode* list2){//赋值头结点structListNode* pcur1 = list1,*pcur2 = list2;//创建哨兵位structListNode* guard =NULL,*tail =NULL; guard = tail =(structListNode*)malloc(sizeof(structListNode)); tail->next =NULL;//将尾结点置空//循环找小,并链接while(pcur1 && pcur2){if(pcur1->val < pcur2->val){ tail->next = pcur1; tail = tail->next; pcur1 = pcur1->next;}else{ tail->next = pcur2; tail = tail->next; pcur2 = pcur2->next;}}//当两个链表有一个走完,就将另一链表链接过来if(pcur1){ tail->next = pcur1;}if(pcur2){ tail->next = pcur2;}//创建一个返回头结点的节点structListNode* head = guard->next;free(guard);//在返回结果前,释放哨兵位return head;}
在这里插入图片描述

三、反转链表

题目链接:反装链表
在这里插入图片描述
题目要求我们将给出的链表反转,就是改变指针的指向,让原本的尾节点变成头,让原本的头结点变成尾

思路一

思路一还是很简单,就是我们创建一个新链表,遍历原链表,拿原链表中的节点头插到新链表就可以了,如图:

有了上图的分析,实现就很简单了,只需要一个头插方法,我们之前讲过,这里就不再赘述了,可以自己试试,我们重点介绍思路二
structListNode*reverseList(structListNode* head){structListNode* cur = head,*newhead =NULL;//赋值当前节点,创建一个新的头结点while(cur){//保存下一个节点structListNode* next = cur->next;//头插 cur->next = newhead; newhead = cur; cur = next;}return newhead;}
在这里插入图片描述

思路二

思路二是对原链表的节点的指针指向进行修改,所以很快,并且不会消耗什么空间,思路如图:
在这里插入图片描述
有了上面的思路我们就可以开始写代码了,但是我们需要注意的一个点就是,如果测试样例是空链表的话,那么我们就还是需要特殊处理一下,如果链表为空,我们就直接返回,因为空链表没有反转的必要,题解如下:
structListNode*reverseList(structListNode* head){//首先对空链表进行判断if(head ==NULL){return head;}structListNode* n1,*n2,*n3; n1 =NULL; n2 = head; n3 = head->next;//当n2为空,结束循环while(n2){ n2->next = n1;//改变指向后,三个指针都向后走 n1 = n2; n2 = n3;//n3不为空就继续向后走if(n3){ n3 = n3->next;}}return n1;}
在这里插入图片描述

四、链表的中间节点

题目链接:链表的中间节点
先来看看题目描述:
在这里插入图片描述
它的要求就是让我们返回链表的中间节点,如果是偶数个节点,那么就有两个中间节点,则返回后一个节点

思路一

第一个很简单的方法就是,先遍历整个链表,看看总共有多少个节点,然后让总数除2,最后再次遍历链表就可以找到中间节点,这个题很简单,我们这里直接给出题解
structListNode*middleNode(structListNode* head){int count =0;//定义计数变量structListNode* pcur = head;while(pcur)//循环计数{ count++; pcur = pcur->next;} count /=2; pcur = head;//重新赋值,找中间结点while(count--){ pcur = pcur->next;}return pcur;//找到中间节点,返回}
在这里插入图片描述

思路二

思路二的方法很绝妙,简单又快捷,就是使用快慢指针的算法,快慢指针默认都指向头结点,慢指针一次走一步,快指针一次走两步,那么等快指针走到空的时候,慢指针指向的节点就是中间节点
为什么呢?因为快指针每次走的距离都是慢指针的2倍,最后统计一共走的距离时,快指针走的总距离也是慢指针的2倍,而快指针走到了空,也就说明走到了链表尾,那么此时慢指针就是它的一半,刚好指向中间节点,题解如下:
思路二的方法很绝白,鉴定饭店有快捷,就是使用我们快慢指针的办法,快慢指针都指向头结点,慢指针一次走一步,快指针一次走两步,那么等快指针走到空的时候,慢指针指向的节点就是中间节点
structListNode*middleNode(structListNode* head){structListNode* fast,* slow; fast = slow = head;while(fast && fast->next)//判断链表是空和快指针走到空的循环条件{ slow = slow->next;//走1步 fast = fast->next->next;//走2步}return slow;//此时慢指针就是中间节点}
在这里插入图片描述

五、综合应用之链表的回文结构

题目链接:链表的回文结构
先来看看题目描述:
在这里插入图片描述
题目要求我们判断给出的链表是否是一个回文结构,也就是是否前后对称,这道题就可以用上我们之前做题写出的代码,具体后面再说,我们先解决一个事情
就是这个题目没有提供C语言的选项,那我们就选择C++来做,C++是兼容C语言的,主要是我们要知道在哪里写代码,如图:
在这里插入图片描述
这是C++中的类,但是不影响我们做题,我们只需要知道我们把代码写在哪里,在题目中也有提示,把代码写在紫色大括号内即可,其它的可以不管,还有一个就是,C++对结构体做了优化,可以在使用结构体时不必加上struct

思路一

虽然判断链表是否是回文结构很难,但是我们可以把链表中的数据存放到数组中,判断数组是否是回文结构,这个就比较简单了
由于链表两边的数据是对称的,所以我们定义一个left和right分别指向数组的头和尾,然后对比它们的值,如果不同直接返回假,否则的话就一直让它们往中间走,直到left不再小于right
在循环过程中,一旦left所在位置的值和right所在位置的值不相同,就说明并不对称,也就不是回文结构,返回假,一旦循环结束,说明左右对称,就是回文结构,直接返回真
并且我们注意到,虽然题目要求空间复杂度为O(1),但是同时又给出了链表的最大节点个数不超过900,那定义一个901个元素大小的数组时间复杂度还是O(1),因为它始终还是常数个空间,如下:
class PalindromeList { public: bool chkPalindrome(ListNode* A){int arr[901]={0}; ListNode* pcur = A;int i =0;//先用数组存储链表的valwhile(pcur){ arr[i++]= pcur->val; pcur = pcur->next;}int left =0, right = i-1;//循环比较数组的valwhile(left < right){if(arr[left]!= arr[right]){return false;//不相同就返回false} left++, right--;//分别向中间走}return true;//走出循环就是回文结构}};

思路二

这个思路可以帮我们复习上面做过的题,让我们能够灵活运用知识,具体思路就是,我们首先通过找中间节点的函数找到链表中间节点,然后从中间节点开始,将后面的节点反转,形成一个新链表,然后再和原链表进行比较即可,如图:
在这里插入图片描述
找中间节点的函数和反转链表的函数可以从我们之前做过的题里面拿过来用,当然也可以自己根据这个逻辑把中间的代码实现,这里我就直接把之前写过的函数直接拿过来用,如下:
class PalindromeList { public:structListNode*reverseList(structListNode* head){structListNode* cur = head,*newhead =NULL;while(cur){//保存下一个节点structListNode* next = cur->next;//头插 cur->next = newhead; newhead = cur; cur = next;}return newhead;}structListNode*middleNode(structListNode* head){structListNode* fast,* slow; fast = slow = head;while(fast && fast->next){ slow = slow->next; fast = fast->next->next;}return slow;} bool chkPalindrome(ListNode* head){structListNode* mid =middleNode(head);structListNode* rhead =reverseList(mid);while(head && rhead){//先判断if(head->val != rhead->val)return false; head = head->next; rhead = rhead->next;}return true;}};
那么今天的Leetcode刷题就到这里了,bye~

Read more

Qwen3-32B+Clawdbot惊艳效果展示:多轮上下文Web对话实测作品集

Qwen3-32B+Clawdbot惊艳效果展示:多轮上下文Web对话实测作品集 1. 这不是普通聊天,是真正“记得住话”的AI对话体验 你有没有试过和一个AI聊着聊着,它突然忘了你三句话前说的关键信息?或者刚让你提供产品参数,转头就问“你刚才说的产品是什么”?这种断裂感,让很多所谓“智能对话”停留在“高级复读机”阶段。 而这次实测的 Qwen3-32B + Clawdbot 组合,彻底打破了这个瓶颈。它不是在模拟理解,而是真正在维持、推理、调用长达20轮以上的上下文记忆——而且是在纯Web界面里,不装插件、不配环境、打开浏览器就能用。 我们没做任何提示词工程优化,没加特殊system message,也没调温度或top-p参数。就是像你平时和同事聊天一样:发问、补充、纠正、追问、切换话题……它全都接得住,还接得稳。 下面这12个真实对话案例,全部来自同一套部署环境下的连续实测记录,未经剪辑、未重试、未人工干预。你可以把它看作一份“

By Ne0inhk
深度解析 WebMCP:让网页成为 AI 智能体的工具库

深度解析 WebMCP:让网页成为 AI 智能体的工具库

深度解析 WebMCP:让网页成为 AI 智能体的工具库 * 深度解析 WebMCP:让网页成为 AI 智能体的工具库 * 前言 * 什么是 WebMCP? * 类比理解 * 为什么要用 WebMCP? * 1. 现有方案的局限性 * 2. WebMCP 的核心优势 * WebMCP 核心概念解析 * 1. 工具(Tools) * 2. 代理(Agent) * 3. 人类在环(Human-in-the-Loop) * 典型使用场景 * 场景一:创意设计助手 * 场景二:智能购物 * 场景三:代码审查 * WebMCP vs 现有方案对比 * 与 MCP 的关系 * 技术架构浅析 * 注册工具的基本模式 * 调用链 * 安全考量 * 1.

By Ne0inhk
WebRTC / HLS / HTTP-FLV 的本质区别与选型指南

WebRTC / HLS / HTTP-FLV 的本质区别与选型指南

在做系统级直播(而不是自己本地播放)时,很多人都会遇到一个经典问题: WebRTC、HLS、HTTP-FLV 到底有什么区别? 项目中到底该选哪个? 传输协议不同 → 延迟不同 → 兼容性 / 稳定性 / 成本不同 在系统里选哪个,核心看两点: 你要多低的延迟?你要多强的兼容和稳定? 一、简介 * WebRTC:超低延迟(0.2 ~ 1s),适合实时监控、无人机、实时指挥 * HLS(hls.js):最稳、最通用(5 ~ 15s),适合活动直播、课程、公开大并发 * HTTP-FLV(flv.js):中低延迟(1 ~ 3s),适合想比 HLS 低延迟,但不想用 WebRTC 的场景(

By Ne0inhk
Flutter 三方库 wasm_ffi 深入鸿蒙端侧硬核 WebAssembly 虚拟机沙盒穿透适配全景:通过异步极速 FFI 中继管道打通底层高算力异构服务-适配鸿蒙 HarmonyOS ohos

Flutter 三方库 wasm_ffi 深入鸿蒙端侧硬核 WebAssembly 虚拟机沙盒穿透适配全景:通过异步极速 FFI 中继管道打通底层高算力异构服务-适配鸿蒙 HarmonyOS ohos

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 wasm_ffi 深入鸿蒙端侧硬核 WebAssembly 虚拟机沙盒穿透适配全景:通过异步极速 FFI 中继管道打通底层高算力异构服务并全面实现无损语言壁垒交互 前言 在 OpenHarmony 应用向高性能计算领域扩展的过程中,如何优雅地接入已有的 C/C++ 算法库(如加密引擎、重型图像处理、数学模拟)而又不失跨平台的便捷性?传统的 NAPI 虽然稳健,但在 Flutter 生态中,直接利用 WebAssembly (WASM) 配合 FFI(External Function Interface)的语义可以在一定程度上实现代码的高度复用。wasm_ffi 库为 Flutter 开发者提供了一套在 Dart 环境下调用 WASM

By Ne0inhk