【LeetCode_21】合并两个有序链表

【LeetCode_21】合并两个有序链表

刷爆LeetCode系列

LeetCode第21题:

github地址

有梦想的电信狗

前言

  • 本文用C++解答LeetCode21

题目描述

题目链接https://leetcode-cn.com/problems/merge-two-sorted-lists/description/

在这里插入图片描述


在这里插入图片描述

题目与思路分析

目标分析

  1. 两个升序链表合并为一个升序链表
  2. 返回新链表的头指针
  3. 新链表中的结点由已有两链表中的节点组成
  4. 提高要求:时间复杂度为O(n),空间复杂度为O(1)

思路一:尾插

思路:创建一个新的空链表newHead同时逐个遍历两个链表的结点,将val较小的节点尾插到新链表中

操作

  • 注意对空链表的处理,两个链表可能都为空,也可能任意一个为空
  • 遍历:循环继续的条件为curNode1 && curNode2,只要有一个链表结束,结束即可,将未遍历完的链表直接整体尾插到新链表中
    • curNode1curNode2:分别用于遍历链表一和链表二
    • tailNode:尾结点,方便尾插时找尾
  • curNode->val <= curNode2->val时,说明:curNode1需要被尾插到新链表
    • 第一次尾插时(if(newHead == nullptr)需要特殊处理
      • tailNode = newHead = curNode;
      • curNode = curNode->next;
    • 其余结点的尾插,常规化处理
      • tailNode->next = curNode;
      • tailNode = tailNode->next;
    • 插入完后:curNode = curNode->next;
  • curNode->val > curNode2->val时和上面是一样的逻辑
  • 循环结束后,检查哪个链表未遍历完全直接整体尾插到新链表中
  • 最终返回return newHead;
// 循环结束后,可能链表还有剩余元素if(curNode1) tailNode->next = curNode1;if(curNode2) tailNode->next = curNode2;
在这里插入图片描述

思路二

思路:使用带哨兵位的头结点优化尾插,在带哨兵位的链表中进行尾插时,无需特殊处理第一次尾插时的情况

操作

  • 注意对空链表的处理,两个链表可能都为空,也可能任意一个为空
  • 遍历:循环继续的条件为curNode1 && curNode2,只要有一个链表结束,结束即可,将未遍历完的链表直接整体尾插到新链表中
    • curNode1curNode2:分别用于遍历链表一和链表二
    • tailNode:尾结点,方便尾插时找尾
  • curNode->val <= curNode2->val时,说明:curNode1需要被尾插到新链表
    • 有了哨兵位的头结点,结点的尾插,都能常规化处理
      • tailNode->next = curNode;
      • tailNode = tailNode->next;
    • 插入完后:curNode = curNode->next;
  • curNode->val > curNode2->val时和上面是一样的逻辑
  • 循环结束后,检查哪个链表未遍历完全直接整体尾插到新链表中
  • 保存新链表的头结点ListNode* newHead = guardNode->next,为guardNodenext结点
    • 释放guardNode防止内存泄露
  • 最终返回新的头结点return newHead;
 guardNode->next =nullptr;delete guardNode 
在这里插入图片描述

代码实现

思路一

classSolution{public: ListNode*mergeTwoLists(ListNode* list1, ListNode* list2){// 空链表判断if(list1 ==nullptr&& list2 ==nullptr)returnnullptr;if(list1 ==nullptr|| list2 ==nullptr){if(list1)return list1;if(list2)return list2;}// 以下 两个链表都不为空 ListNode* curNode1 = list1; ListNode* curNode2 = list2; ListNode* newHead =nullptr,*tailNode =nullptr;while(curNode1 && curNode2){// 更小的元素尾插到新结点if(curNode1->val <= curNode2->val){// 第一个节点直接尾插if(newHead ==nullptr){ newHead = tailNode = curNode1;}// 其他节点直接 尾插else{ tailNode->next = curNode1; tailNode = tailNode->next;} curNode1 = curNode1->next;}else{// 第一个节点直接尾插if(newHead ==nullptr){ newHead = curNode2; tailNode = curNode2;}// 其他节点直接 尾插else{ tailNode->next = curNode2; tailNode = tailNode->next;} curNode2 = curNode2->next;}}// 循环结束后,可能链表还有剩余元素if(curNode1) tailNode->next = curNode1;if(curNode2) tailNode->next = curNode2;return newHead;}};

思路二

// 使用带哨兵位的头结点 优化算法classSolution{public: ListNode*mergeTwoLists(ListNode* list1, ListNode* list2){if(list1 ==nullptr)return list2;if(list2 ==nullptr)return list1; ListNode* curNode1 = list1,*curNode2 = list2;// 遍历链表,用于迭代// 尾插需要找尾,提前保存,避免重复找 ListNode* guardNode =newListNode(); ListNode* tailNode = guardNode;while(curNode1 && curNode2){// 将小的那个结点,尾插到 guardNode 后面if(curNode1->val <= curNode2->val){ tailNode->next = curNode1; tailNode = tailNode->next; curNode1 = curNode1->next;}else{ tailNode->next = curNode2; tailNode = tailNode->next; curNode2 = curNode2->next;}}// 有一个链表结束后,将另一个链表再链接上if(curNode1) tailNode->next = curNode1;if(curNode2) tailNode->next = curNode2;// 保存新的头结点,并释放内存,防止内存泄露 ListNode* newHead = guardNode->next; guardNode->next =nullptr;delete guardNode;// 返回新结点return newHead;}};

算法代码优化

优化思路一

  • 优化了空链表返回的逻辑
  • 其中一个链表为空,就返回另一个链表
classSolution{public: ListNode*mergeTwoLists(ListNode* list1, ListNode* list2){if(list1 ==nullptr)return list2;if(list2 ==nullptr)return list1;// 以下 两个链表都不为空 ListNode* curNode1 = list1; ListNode* curNode2 = list2; ListNode* newHead =nullptr,*tailNode =nullptr;while(curNode1 && curNode2){// 更小的元素尾插到新结点if(curNode1->val <= curNode2->val){// 第一个节点直接尾插if(newHead ==nullptr){ newHead = tailNode = curNode1;}// 其他节点直接 尾插else{ tailNode->next = curNode1; tailNode = tailNode->next;} curNode1 = curNode1->next;}else{// 第一个节点直接尾插if(newHead ==nullptr){ newHead = curNode2; tailNode = curNode2;}// 其他节点直接 尾插else{ tailNode->next = curNode2; tailNode = tailNode->next;} curNode2 = curNode2->next;}}// 循环结束后,可能链表还有剩余元素if(curNode1) tailNode->next = curNode1;if(curNode2) tailNode->next = curNode2;return newHead;}};

优化思路二

  • 优化了初始链表判空的处理
  • 这里无需对空链表进行处理:通过分析得知
    • list1list2nullptr时,不会进入while循环。由于guardNode非空, ListNode* newHead = guardNode->next,因此保存的newHead时一定合法。
    • list1list2全为nullptr时,newHead即为nullptr
    • list1list2其中一个为nullptr时,newHead即另一个链表的头结点
// 使用带哨兵位的头结点 优化算法classSolution{public: ListNode*mergeTwoLists(ListNode* list1, ListNode* list2){// 遍历链表的结点 ListNode* curNode1 = list1,*curNode2 = list2;// 尾插需要找尾,提前保存,避免重复找 ListNode* guardNode =newListNode(); ListNode* tailNode = guardNode;while(curNode1 && curNode2){// 将小的那个结点,尾插到 guardNode 后面if(curNode1->val <= curNode2->val){ tailNode->next = curNode1; tailNode = tailNode->next; curNode1 = curNode1->next;}else{ tailNode->next = curNode2; tailNode = tailNode->next; curNode2 = curNode2->next;}}// 有一个链表结束后,将另一个链表再链接上if(curNode1) tailNode->next = curNode1;if(curNode2) tailNode->next = curNode2;// 保存新的头结点,并释放内存,防止内存泄露 ListNode* newHead = guardNode->next; guardNode->next =nullptr;delete guardNode;// 返回新结点return newHead;}};

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

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

Read more

Flutter 组件 tree_iterator 适配鸿蒙 HarmonyOS 实战:高性能树状数据遍历,构建海量节点递归优化与分布式层级调度架构

Flutter 组件 tree_iterator 适配鸿蒙 HarmonyOS 实战:高性能树状数据遍历,构建海量节点递归优化与分布式层级调度架构

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 组件 tree_iterator 适配鸿蒙 HarmonyOS 实战:高性能树状数据遍历,构建海量节点递归优化与分布式层级调度架构 前言 在鸿蒙(OpenHarmony)生态迈向万物智联、涉及海量传感器拓扑映射、复杂 UI 树状 DOM 解析及超大型目录层级处理的背景下,如何实现高效、内存友好的“非线性数据遍历”,已成为决定应用数据发现效率与算法性能表现的基石。在鸿蒙设备这类强调 AOT 极致性能与低堆内存占用的环境下,如果应用依然采用简单的递归(Recursion)进行深度数据挖掘,由于由于树状结构深度的不可控性,极易由于由于“栈溢出(Stack Overflow)”或“重复解析”导致系统的瞬时崩卡。 我们需要一种能够解耦数据结构与遍历逻辑、支持深度/广度优先算法且具备“零样板代码”调用的迭代器方案。 tree_iterator 为

By Ne0inhk

Flutter 三方库 super_dates 的鸿蒙化适配指南 - 在鸿蒙系统上构建极致、强类型、更优雅的 DateTime 增强与时间逻辑审计引擎

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.ZEEKLOG.net Flutter 三方库 super_dates 的鸿蒙化适配指南 - 在鸿蒙系统上构建极致、强类型、更优雅的 DateTime 增强与时间逻辑审计引擎 在鸿蒙(OpenHarmony)系统的日程管理、精密任务调度(如鸿蒙版闹钟/日历)、理财工具或带有复杂时间区间(Periods)计算的应用中,如何摆脱标准 DateTime 库中那些模糊的整数偏移,转而使用语义明确、强类型保障的现代日期 API?super_dates 为开发者提供了一套工业级的、基于 Extension 的 DateTime 深度增强方案。本文将深入实战其在鸿蒙时间维度逻辑层中的应用。 前言 什么是 SuperDates?它不是一个替代 DateTime 的庞大框架,而是对 Dart 原生时间类的一次“极致外科手术级”

By Ne0inhk
【AIGC】内容创作——AI文字、图像、音频和视频的创作流程

【AIGC】内容创作——AI文字、图像、音频和视频的创作流程

我的主页:2的n次方_       近年来,生成式人工智能(AIGC,Artificial Intelligence Generated Content)技术迅速发展,彻底改变了内容创作的各个领域。无论是文字、图像、音频,还是视频,AI都在推动着创作流程的颠覆性变革。本文将详细介绍AIGC在内容创作中的应用,并分析其背后的技术及对未来的影响。 1. 什么是AIGC? AIGC,即人工智能生成内容,是指通过机器学习模型生成各种形式的内容。与传统的人工创作不同,AIGC可以通过对大量数据的分析与学习,自动生成文字、图像、音频、视频等多种形式的内容。 AIGC的核心技术依赖于深度学习模型,如生成对抗网络(GANs)、自回归模型(如GPT)、自动编码器(VAE),以及多模态AI模型。它们能够理解和模仿不同数据模式,生成高质量的原创内容。 2. AIGC文字创作 2.1 自然语言生成(NLG) AIGC的最大突破之一是自然语言生成(NLG),如OpenAI的GPT模型系列,它们通过训练大规模语言模型,生成流畅的文章、

By Ne0inhk