剑指Offer - 面试题25:合并俩个排序的链表

2023-11-19

题目

输入俩个递增排序的链表,合并这俩个链表并使新链表中的节点仍然是递增序列。例如下图链表1和链表2,合并后的升序链表为链表3,链表节点定义如下:

typedef int TElemType;//链表节点值的数据类型
struct ListNode
{
    TElemType m_nValue;
    ListNode* m_pNext;
};

在这里插入图片描述

分析

我们可以构造出来一个链表。表头不存储数据,定义俩指针p1p2分别指向链表1和链表2表头。再创建一个记录新的链表尾部的指针previously。然后对比俩指针所指节点的值,谁小取谁的值,然后对应的指针后移。将新的节点连接到previously指向的节点后面,然后更新previously。直到俩个链表有一个为nullptr或者俩个均为nullptr
当有一个链表为空退出循环时,直接将previously的下一个节点连接到另一个链表指针的节点。若均为nullptr时,不进行任何操作。(但是如果题目要求不允许修改原链表,你就只能继续遍历链表,然后创建新节点,连接。)

下面代码是不允许修改的原链表代码,且未优化
C++

#include <iostream>
using namespace std;

typedef int TElemType;//链表节点值的数据类型
struct ListNode
{
    TElemType m_nValue;
    ListNode* m_pNext;
};

ListNode* CreateNode(TElemType val)//创建节点
{
    ListNode* Node = new ListNode();
    Node->m_nValue = val;
    Node->m_pNext = nullptr;
    return Node;
}

void connect(ListNode* L1, ListNode* L2)//链接
{
    L1->m_pNext = L2;
}

void PrintList(ListNode* head)
{
    while (head != nullptr)
    {
        cout << head->m_nValue;
        if (head->m_pNext != nullptr)
        {
            cout << "->";
        }
        else
        {
            cout << endl;
        }
        head = head->m_pNext;
    }
}

ListNode* Merge(ListNode* pHead1, ListNode* pHead2)//合并俩链表
{
    if (nullptr == pHead1 && nullptr == pHead2)
    {
       return nullptr;
    }

    if (nullptr == pHead1)
    {
        return pHead2;
    }

    if (nullptr == pHead2)
    {
        return pHead1;
    }

    ListNode* p1 = pHead1;
    ListNode* p2 = pHead2;
    ListNode* NewNode = nullptr;//新创建的节点指针

    ListNode* head = new ListNode();//新链表的表头指针
    head->m_nValue = 0;
    head->m_pNext = nullptr;
    ListNode* previously = head;//表尾指针

    
    while (p1 != nullptr && p2 != nullptr)
    {
        int min = 0;//用于保存小值的节点

        //找最小值
        if (p1->m_nValue < p2->m_nValue)
        {
            min = p1->m_nValue;
            p1 = p1->m_pNext;
        }
        else
        {
            min = p2->m_nValue;
            p2 = p2->m_pNext;
        }

        //创建新节点
        NewNode = new ListNode();
        NewNode->m_nValue = min;
        NewNode->m_pNext = nullptr;

        //连接
        previously->m_pNext = NewNode;

        //更新链表尾指针
        previously = NewNode;
    }

    /*不可修改原链表的代码*/
    while (p1 != nullptr)
    {
        int min = p1->m_nValue;
        p1 = p1->m_pNext;
        //创建新节点
        NewNode = new ListNode();
        NewNode->m_nValue = min;
        NewNode->m_pNext = nullptr;

        //连接
        previously->m_pNext = NewNode;

        //更新链表尾指针
        previously = NewNode;
    }

    /*不可修改原链表的代码*/
    while (p2 != nullptr)
    {
        int min = p2->m_nValue;
        p2 = p2->m_pNext;
        //创建新节点
        NewNode = new ListNode();
        NewNode->m_nValue = min;
        NewNode->m_pNext = nullptr;

        //连接
        previously->m_pNext = NewNode;

        //更新链表尾指针
        previously = NewNode;
    }
    return head->m_pNext;
}


int main()
{
    /*测试用例1*/
    // 1 3 5 7       2 4 6 8
    ListNode* Node1 = CreateNode(1);
    ListNode* Node3 = CreateNode(3);
    ListNode* Node5 = CreateNode(5);
    ListNode* Node7 = CreateNode(7);

    ListNode* Node2 = CreateNode(2); 
    ListNode* Node4 = CreateNode(4); 
    ListNode* Node6 = CreateNode(6);
    ListNode* Node8 = CreateNode(8);

    connect(Node1, Node3);
    connect(Node3, Node5);
    connect(Node5, Node7);

    connect(Node2, Node4);
    connect(Node4, Node6);
    connect(Node6, Node8);

    cout << "链表1:";
    PrintList(Node1);
    cout << "链表2:";
    PrintList(Node2);

    ListNode* head = Merge(Node1, Node2);
    cout << "合并后的链表:";
    PrintList(head);
    cout << "--------------------------------------" << endl;

    /*测试用例2*/
    // 1 2 4      3 5 8  
    Node1 = CreateNode(1);
    Node2 = CreateNode(2);
    Node4 = CreateNode(4);

    Node3 = CreateNode(3);
    Node5 = CreateNode(5);
    Node8 = CreateNode(8);

    connect(Node1, Node2);
    connect(Node2, Node4);

    connect(Node3, Node5);
    connect(Node5, Node8);

    cout << "链表1:";
    PrintList(Node1);
    cout << "链表2:";
    PrintList(Node3);

    head = Merge(Node1, Node3);
    cout << "合并后的链表:";
    PrintList(head);


    return 0;
}

在这里插入图片描述

上述代码中发现创建新节点多次重复我们可以封装成一个函数

		int min = p2->m_nValue;
        p2 = p2->m_pNext;
        //创建新节点
        NewNode = new ListNode();
        NewNode->m_nValue = min;
        NewNode->m_pNext = nullptr;

        //连接
        previously->m_pNext = NewNode;

        //更新链表尾指针
        previously = NewNode;

优化后的不允许修改原链表代码
C++

#include <iostream>
using namespace std;

typedef int TElemType;//链表节点值的数据类型
struct ListNode
{
	TElemType m_nValue;
	ListNode* m_pNext;
};

ListNode* CreateNode(TElemType val)//创建节点
{
	ListNode* Node = new ListNode();
	Node->m_nValue = val;
	Node->m_pNext = nullptr;
	return Node;
}

void connect(ListNode* L1, ListNode* L2)//链接
{
	L1->m_pNext = L2;
}

void PrintList(ListNode* head)//输出
{
	while (head != nullptr)
	{
		cout << head->m_nValue;
		if (head->m_pNext != nullptr)
		{
			cout << "->";
		}
		else
		{
			cout << endl;
		}
		head = head->m_pNext;
	}
}

//创建并连接新节点
//此处传参必须要二级指针,因为要修改指针
void connNewNode(ListNode** previously, ListNode** p)
{
	//创建新节点
	ListNode* NewNode = new ListNode();
	NewNode->m_nValue = (*p)->m_nValue;
	NewNode->m_pNext = nullptr;

	//连接
	(*previously)->m_pNext = NewNode;

	//更新新链表尾指针
	*previously = NewNode;

	//更新原链表指针
	*p = (*p)->m_pNext;
}

ListNode* Merge(ListNode* pHead1, ListNode* pHead2)//合并俩链表
{
	if (nullptr == pHead1)
	{
		return pHead2;
	}

	if (nullptr == pHead2)
	{
		return pHead1;
	}

	ListNode* p1 = pHead1;
	ListNode* p2 = pHead2;

	ListNode* head = new ListNode();//新链表的表头指针
	head->m_nValue = 0;
	head->m_pNext = nullptr;
	ListNode* previously = head;//表尾指针


	while (p1 != nullptr && p2 != nullptr)
	{
		if (p1->m_nValue < p2->m_nValue)
		{
			connNewNode(&previously, &p1);
		}
		else
		{
			connNewNode(&previously, &p2);
		}
	}

	/*不可修改原链表的代码*/
	while (p1 != nullptr)
	{
		connNewNode(&previously, &p1);
	}

	/*不可修改原链表的代码*/
	while (p2 != nullptr)
	{
		connNewNode(&previously, &p2);
	}
	return head->m_pNext;
}


int main()
{
	/*测试用例1*/
	// 1 3 5 7       2 4 6 8
	ListNode* Node1 = CreateNode(1);
	ListNode* Node3 = CreateNode(3);
	ListNode* Node5 = CreateNode(5);
	ListNode* Node7 = CreateNode(7);

	ListNode* Node2 = CreateNode(2);
	ListNode* Node4 = CreateNode(4);
	ListNode* Node6 = CreateNode(6);
	ListNode* Node8 = CreateNode(8);

	connect(Node1, Node3);
	connect(Node3, Node5);
	connect(Node5, Node7);

	connect(Node2, Node4);
	connect(Node4, Node6);
	connect(Node6, Node8);

	cout << "链表1:";
	PrintList(Node1);
	cout << "链表2:";
	PrintList(Node2);

	ListNode* head = Merge(Node1, Node2);
	cout << "合并后的链表:";
	PrintList(head);
	cout << "--------------------------------------" << endl;

	/*测试用例2*/
	// 1 2 4      3 5 8  
	Node1 = CreateNode(1);
	Node2 = CreateNode(2);
	Node4 = CreateNode(4);

	Node3 = CreateNode(3);
	Node5 = CreateNode(5);
	Node8 = CreateNode(8);

	connect(Node1, Node2);
	connect(Node2, Node4);

	connect(Node3, Node5);
	connect(Node5, Node8);

	cout << "链表1:";
	PrintList(Node1);
	cout << "链表2:";
	PrintList(Node3);

	head = Merge(Node1, Node3);
	cout << "合并后的链表:";
	PrintList(head);

	return 0;
}

如果我们可以修改原链表,代码将更加简单。由于只修改了Merge函数,所以展示部分代码
C++

ListNode* Merge(ListNode* pHead1, ListNode* pHead2)//合并俩链表
{
	if (nullptr == pHead1)
	{
		return pHead2;
	}

	if (nullptr == pHead2)
	{
		return pHead1;
	}

	ListNode* p1 = pHead1;
	ListNode* p2 = pHead2;

	ListNode* head = new ListNode();//新链表的表头指针
	head->m_nValue = 0;
	head->m_pNext = nullptr;
	ListNode* previously = head;//表尾指针


	while (p1 != nullptr && p2 != nullptr)
	{
		if (p1->m_nValue < p2->m_nValue)
		{
			connNewNode(&previously, &p1);
		}
		else
		{
			connNewNode(&previously, &p2);
		}
	}

	/*可修改原链表的代码*/
	if (p1 != nullptr)
	{
		previously->m_pNext = p1;
	}

	/*可修改原链表的代码*/
	if (p2 != nullptr)
	{
		previously->m_pNext = p2;
	}

	return head->m_pNext;
}

在这里插入图片描述

本章完!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

剑指Offer - 面试题25:合并俩个排序的链表 的相关文章

  • C++ 中的软(不是:弱)引用 - 这可能吗?有实施吗?

    在 C 中我正在使用boost shared ptr and boost weak ptr自动删除不再需要的对象 我知道这些与引用计数一起工作 在 Java 中 内存由垃圾收集器管理 它将内置对象引用视为strong WeakReferen
  • 当我单击 C# 中的“取消”按钮时重定向到新页面(Web 部分)

    Cancel button tc new TableCell btnCancel new Button btnCancel Text Cancel btnCancel Click new EventHandler btnCanel Clic
  • 在 OpenCL 中将函数作为参数传递

    是否可以在 OpenCL 1 2 中将函数指针传递给内核 我知道可以用C实现 但不知道如何在OpenCL的C中实现 编辑 我想做这篇文章中描述的同样的事情 在 C 中如何将函数作为参数传递 https stackoverflow com q
  • 捕获 foreach 条件中抛出的异常

    我有一个foreach在 foreach 本身的条件下循环期间中断的循环 有没有办法try catch抛出异常然后继续循环的项 这将运行几次 直到异常发生然后结束 try foreach b in bees exception is in
  • 通信对象 System.ServiceModel.Channels.ServiceChannel 不能用于通信

    通信对象System ServiceModel Channels ServiceChannel 无法用于通信 因为它处于故障状态 这个错误到底是什么意思 我该如何解决它 您收到此错误是因为您让服务器端发生 NET 异常 并且您没有捕获并处理
  • Linux TUN/TAP:无法从 TAP 设备读回数据

    问题是关于如何正确配置想要使用 Tun Tap 模块的 Linux 主机 My Goal 利用现有的路由软件 以下为APP1和APP2 但拦截并修改其发送和接收的所有消息 由Mediator完成 我的场景 Ubuntu 10 04 Mach
  • std::map 和二叉搜索树

    我读过 std map 是使用二叉搜索树数据结构实现的 BST 是一种顺序数据结构 类似于数组中的元素 它将元素存储在 BST 节点中并按其顺序维护元素 例如如果元素小于节点 则将其存储在节点的左侧 如果元素大于节点 则将其存储在节点的右侧
  • ZLIB 解压缩

    我编写了一个小型应用程序 该应用程序应该解压缩以 gzip deflate 格式编码的数据 为了实现这一点 我使用 ZLIB 库 使用解压缩功能 问题是这个功能不起作用 换句话说 数据不是未压缩的 我在这里发布代码 int decompre
  • Xamarin Android:获取内存中的所有进程

    有没有办法读取所有进程 而不仅仅是正在运行的进程 如果我对 Android 的理解正确的话 一次只有一个进程在运行 其他所有进程都被冻结 后台进程被忽略 您可以使用以下代码片段获取当前正在运行的所有 Android 应用程序进程 Activ
  • C++派生模板类继承自模板基类,无法调用基类构造函数[重复]

    这个问题在这里已经有答案了 我试图从基类 模板 继承 派生类也是模板 它们具有相同的类型 T 我收到编译错误 非法成员初始化 Base 不是基类或成员 为什么 如何调用基类构造函数 include
  • C# 创建数组的数组

    我正在尝试创建一个将使用重复数据的数组数组 如下所示 int list1 new int 4 1 2 3 4 int list2 new int 4 5 6 7 8 int list3 new int 4 1 3 2 1 int list4
  • std::bind 重载解析

    下面的代码工作正常 include
  • 在 C 中复制两个相邻字节的最快方法是什么?

    好吧 让我们从最明显的解决方案开始 memcpy Ptr const char a b 2 调用库函数的开销相当大 编译器有时不会优化它 我不会依赖编译器优化 但即使 GCC 很聪明 如果我将程序移植到带有垃圾编译器的更奇特的平台上 我也不
  • UWP 无法在两个应用程序之间创建本地主机连接

    我正在尝试在两个 UWP 应用程序之间设置 TCP 连接 当服务器和客户端在同一个应用程序中运行时 它可以正常工作 但是 当我将服务器部分移动到一个应用程序并将客户端部分移动到另一个应用程序时 ConnectAsync 会引发异常 服务器未
  • 从匿名类型获取值

    我有一个方法如下 public void MyMethod object obj implement 我这样称呼它 MyMethod new myparam waoww 那么我该如何实施MyMethod 获取 myparam 值 Edit
  • C# 搜索目录中包含字符串的所有文件,然后返回该字符串

    使用用户在文本框中输入的内容 我想搜索目录中的哪个文件包含该文本 然后我想解析出信息 但我似乎找不到该字符串或至少返回信息 任何帮助将不胜感激 我当前的代码 private void btnSearchSerial Click object
  • 过期时自动重新填充缓存

    我当前缓存方法调用的结果 缓存代码遵循标准模式 如果存在 则使用缓存中的项目 否则计算结果 在返回之前将其缓存以供将来调用 我想保护客户端代码免受缓存未命中的影响 例如 当项目过期时 我正在考虑生成一个线程来等待缓存对象的生命周期 然后运行
  • 32位PPC rlwinm指令

    我在理解上有点困难rlwinmPPC 汇编指令 旋转左字立即然后与掩码 我正在尝试反转函数的这一部分 rlwinm r3 r3 0 28 28 我已经知道什么了r3 is r3在本例中是一个 4 字节整数 但我不确定这条指令到底是什么rlw
  • Fluent NHibernate 日期时间 UTC

    我想创建一个流畅的 nhibernate 映射来通过以下方式映射 DateTime 字段 保存时 保存 UTC 值 读取时 调整为本地时区值 实现此映射的最佳方法是什么 就我个人而言 我会将日期存储在 UTC 格式的对象中 然后在读 写时在
  • 为什么 Ajax.BeginForm 在 Chrome 中不起作用?

    我正在使用 c NET MVC2 并尝试创建一个 ajax 表单来调用删除数据库记录 RemoveRelation 的方法 删除记录的过程正在按预期进行 删除记录后 表单应调用一个 JavaScript 函数 从视觉效果中删除该记录 Rem

随机推荐

  • element ui的el-tree多选树(复选框)父子节点关联不关联的问题,选中当前节点,他的子节点和父节点是否被选中,非常详细

    element ui的el tree多选树 复选框 父子节点关联不关联的问题 选中当前节点 他的子节点和父节点是否被选中 非常详细 属性check strictly 官方文档提供属性check strictly 在显示复选框的情况下 是否严
  • SQL Server2012 安装方法详解

    欢迎大家关注我的公众号 添加我为好友 首先要找到自己下载好的安装包 并且保持网络畅通 最近有不少细心的小伙伴反应安装包有问题 我这里进行了一下更新 链接 https pan baidu com s 1bB WS zmHy ow34mU ET
  • scss的基本语法的使用

    scss 1 声明变量 声明变量符 变量名称 变量值 eg width300 300px 2 变量调用 width300 300px 声明 main width width300 调用 3 局部变量和全局变量 定义在局部的变量不会影响其他的
  • 关于不重启Tomcat自动加载改变的class文件

    修改server xml 在Host标签下加入以下配置
  • cmake 怎么设定 prefix ?

    cmake D CMAKE INSTALL PREFIX usr
  • 分页节点

    动态调度技术 分页数据库 osg PageLOD 动态调度技术 如果数据庞大 那么是不可能一次性全部载入内存的 因此需要动态调度技术 动态调度技术 在显示当前视域中的场景元素的同时 预判下一步可能载入的数据 以及那些短时间内不会被看到的数据
  • win11共享打印机无法连接怎么办

    很多小伙伴都将电脑更新升级成Win11系统 当我们使用多台电脑却只有一台打印机时 就需要共享打印机却出现了Win11共享打印机无法连接的情况 遇到这种问题应该怎么解决呢 下面小编就给大家详细介绍一下Win11共享打印机无法连接的解决方法 大
  • hexo tags-标签设置-aloha

    打开博客界面里面的 config yml进行配置 打开如下 找到类似于下面这些的代码行 Directory source dir source public dir public tag dir tags 标签 archive dir ar
  • MySQL-group_concat()

    创建表格 CREATE TABLE dept emp emp no int 11 NOT NULL dept no char 4 NOT NULL from date date NOT NULL to date date NOT NULL
  • expandableListview实现侧滑删除

    本文地址 http blog csdn net xiehao 95 article details 44628491 使用swipelistview实现侧滑删除这样Demo已经很普及了 但是项目需要 expandableListview的i
  • 复习整理 Mask R-CNN

    理解Mask R CNN 文章目录 理解Mask R CNN 前言 一 简介 基础点 名词解释 简单复习 前言 为了综合复习 Mask R CNN 写一个博客 简言之 物体检测 产生一个切割mask 识别 和FasterR CNN区别 能生
  • spark-3.1.2兼容多版本hive

    2 3 9版本Hive的支持 直接在实例化SparkSession时 启用hive支持即可 例如 val spark SparkSession builder appName Spark Hive Example config spark
  • ThinkPHP 5 框架实现多语言 实例讲解

    ThinkPHP 5 框架实现多语言 今天给大家分享一篇tp5框架多语言的实例 第一步 您需要在配置文件中开启网站多语言 并添加语言允许列表 默认语言 default lang gt zh cn 语言允许列表 lang list gt zh
  • Oracle 用户锁定,解决办法

    启动cmd 窗口指令 切换中oracle 默认超级管理员账户 oracle 默认登入超级管理员 C Users Administratir gt sqlplus as sysdba 为admin 用户解锁 SQL gt alter user
  • CLIP-as-service 0.8.0 版本发布:新增支持大型 ONNX 模型文件

    CLIP as service 是一种用于编码图像和文本的低延迟 高可扩展性服务 它可以作为微服务轻松集成到神经搜索解决方案中 CLIP as service 0 8 0 现已正式发布 本次更新包含 3 个新增功能 1 个性能改进 1 个文
  • typeof 与 instanceof ,如何模拟实现一个 instanceof,有没有通用检测数据类型?

    书山有路勤为径 学海无涯苦作舟 金三银四 面试加油 冲 一 typeof 1 typeof 优点 缺点 优点 能够快速区分基本数据类型 缺点 不能将Object Array和Null区分 都返回object 2 typeof 作用 区分数据
  • 运营商线路细分_呼叫中心各种线路的区分

    对于一个呼叫中心系统来说 最重要的一部分当然是线路 关系着电话的呼出和呼入 那么你了解什么是呼叫中心的线路吗 今天来给大家普及一下市面上常见的一些线路 以及线路的区别 呼叫系统的线路其实就是呼入与呼出的电话号码 只是会以不同的方式接入到系统
  • 【经验分享】Windows/Ubuntu上如何使用api下载kaggle上的数据集

    1 下载kaggle的api 1 1 已经安装了Anaconda 打开cmd Windows 打开终端 Ubuntu conda activate 你的conda环境名称 这里我的环境叫做Pytorch conda activate Pyt
  • Spark的常用概念总结

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 前言 一 基本概念 1 RDD的生成 2 RDD的存储 3 Dependency 4 Transformation和Action 4 1 Transformatio
  • 剑指Offer - 面试题25:合并俩个排序的链表

    题目 输入俩个递增排序的链表 合并这俩个链表并使新链表中的节点仍然是递增序列 例如下图链表1和链表2 合并后的升序链表为链表3 链表节点定义如下 typedef int TElemType 链表节点值的数据类型 struct ListNod