剑指offer 学习笔记 复杂链表的复制

2023-11-13

分治法:把分解后的小问题各个解决,然后把小问题的解决方案结合起来解决大问题。

面试题35:复杂链表的复制。请实现函数ComplexListNode* Clone(ComplexListNode* pHead),复制一个复杂链表。在复杂链表中,每个节点除了有一个m_pNext指针指向下一个节点,还有一个m_pSibling指针指向链表中任意节点或者nullptr。节点定义如下:

struct ComplexListNode {
    int m_nValue;
    ComplexListNode* m_pNext;
    ComplexListNode* m_pSibling;
};

这个问题很多人的方法是把复制过程分为两步,第一步复制原始链表上的每个节点,再用m_pNext链接起来;第二步是设置每个节点的m_pSibling指针。对于第二步,找到一个节点的m_pSibling指针指向的节点需要从头结点沿着m_pNext遍历链表,因为我们不知道m_pSibling指向的节点在当前节点的前面还是后面。对于一个含有n个结点的复杂链表,这种方法的总的时间复杂度是O(n²)。

以上方法的时间主要花费在定位节点的m_pSibling指针指向的节点上,我们可以优化第二步:将原始链表的节点N和新链表的节点n放到一个哈希表中,用<N,n>表示,若原始链表中节点N的m_pSibling指向节点S,那么新链表中节点n的m_pSibling指向s,有了哈希表,我们可以用O(1)的时间根据S找到s。这种方法相当于以空间换时间,对于n个节点的复杂链表,我们需要一个大小为O(n)的哈希表,把时间复杂度降为了O(n)。

还有一种思路,可以不用辅助空间就实现O(n)的时间效率。第一步,将根据原始节点创建的新节点插入到原始节点后边;第二步设置新创建节点的m_pSibling指针,如原始链表上节点N的m_pSibling指向S,则它的对应复制节点n的m_pSibling指向s;第三步把长链表拆分为两个链表:

#include <iostream>
#include <stack>
using namespace std;

struct ComplexListNode {
    int m_nValue;
    ComplexListNode* m_pNext;
    ComplexListNode* m_pSibling;
};

void printListReversingly_iteratively(ComplexListNode* pHead) {
    stack<ComplexListNode*> nodes;

    ComplexListNode* pNode = pHead;
    while (pNode != nullptr) {    // 将链表从头到尾压栈
        nodes.push(pNode);
        pNode = pNode->m_pNext;
    }

    while (!nodes.empty()) {    // 打印栈中内容
        cout << nodes.top()->m_nValue << endl;    // 打印栈顶值
        nodes.pop();    // 栈顶元素出栈
    }
}

void CloneNodes(ComplexListNode* pHead) {    // 第一步,克隆节点
    while (pHead) {
        ComplexListNode* pNode = new ComplexListNode();
        pNode->m_nValue = pHead->m_nValue;
        pNode->m_pSibling = pHead->m_pSibling;    // 给pNode赋值
       
        pNode->m_pNext = pHead->m_pNext;    
        pHead->m_pNext = pNode;    // 将pNode插入到链表

        pHead = pNode->m_pNext;    // 继续复制下一个节点
    }
}

void ConnectSiblingNode(ComplexListNode* pHead) {    // 第二步,设置新节点的m_pSibling指针
    while (pHead) {
        if (pHead->m_pSibling != nullptr) {    // 当pHead的m_pSibling指针不为空时
            pHead->m_pNext->m_pSibling = pHead->m_pSibling->m_pNext;
        }
        pHead = pHead->m_pNext->m_pNext;
    }
}

ComplexListNode* ReconnectNodes(ComplexListNode* pHead) {    // 第三步,分链表
    ComplexListNode* newList = pHead->m_pNext, *oldList = pHead;    // 分成的两个链表的头节点
    ComplexListNode* pNewNode, *pOldNode = pHead;    // 分别指向新旧两个链表中的节点
    while (pOldNode->m_pNext->m_pNext) {    // 当原链表节点没结束时
        pNewNode = pOldNode->m_pNext;    // 更新新节点为下一个新节点
        pOldNode->m_pNext = pNewNode->m_pNext;    // 将指向新节点的指针指向新节点的后一个节点
        pNewNode->m_pNext = pNewNode->m_pNext->m_pNext;    // 将新节点的后继指针指向下一个新节点
        pOldNode = pOldNode->m_pNext;
    }
    pOldNode->m_pNext = nullptr;    // 因为循环只能到最后两个新旧节点还没分开的状态,需要将最后一个旧节点与最后一个新节点分开

    cout << "反向打印旧链表验证旧链表状态:" << endl;
    printListReversingly_iteratively(oldList);

    return newList;
}

ComplexListNode* Clone(ComplexListNode* pHead) {
    if (pHead == nullptr) {
        return nullptr;
    }

    CloneNodes(pHead);    // 第一步,克隆节点
    ConnectSiblingNode(pHead);    // 第二步,设置新节点的m_pSibling指针
    return ReconnectNodes(pHead);    // 第三步,分链表
}

int main() {
    ComplexListNode* pNode1 = new ComplexListNode();
    ComplexListNode* pNode2 = new ComplexListNode();
    ComplexListNode* pNode3 = new ComplexListNode();
    ComplexListNode* pNode4 = new ComplexListNode();
    ComplexListNode* pNode5 = new ComplexListNode();

    pNode1->m_nValue = 1;
    pNode2->m_nValue = 2;
    pNode3->m_nValue = 3;
    pNode4->m_nValue = 4;
    pNode5->m_nValue = 5;

    pNode1->m_pNext = pNode2;
    pNode2->m_pNext = pNode3;
    pNode3->m_pNext = pNode4;
    pNode4->m_pNext = pNode5;
    pNode5->m_pNext = nullptr;

    pNode1->m_pSibling = pNode3;
    pNode2->m_pSibling = pNode5;
    pNode3->m_pSibling = nullptr;
    pNode4->m_pSibling = pNode2;
    pNode5->m_pSibling = nullptr;

    ComplexListNode *pHead =  Clone(pNode1);

    cout << "反向打印新链表:" << endl;
    printListReversingly_iteratively(pHead);

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

剑指offer 学习笔记 复杂链表的复制 的相关文章

  • 【NOIP2012】开车旅行(倍增)

    题面 Description 小A 和小B决定利用假期外出旅行 他们将想去的城市从1到N 编号 且编号较小的城市在编号较大的城市的西边 已知各个城市的海拔高度互不相同 记城市 i的海拔高度为Hi 城市 i 和城市 j 之间的距离 d i j
  • var、let、const的区别

    var let const的区别 大体区别 const定义的变量不可以修改 而且必须初始化 var定义的变量可以修改 如果不初始化会输出undefined 不会报错 var定义的变量没有块的概念 可以跨酷爱访问 不能跨函数访问 let是块级
  • 【华为OD机试真题】投篮大赛(C++&java&python)100%通过率 超详细代码注释 代码优化

    华为OD机试真题 2022 2023 真题目录 点这里 华为OD机试真题 信号发射和接收 试读 点这里 华为OD机试真题 租车骑绿道 试读 点这里 投篮大赛 知识点字符串 时间限制 1s空间限制 256MB限定语言 不限 题目描述 你现在是
  • dbnet训练_1215

    https blog csdn net hhhhhhhhhhwwwwwwwwww article details 123904386 ops request misc 257B 2522request 255Fid 2522 253A 25
  • java使用jdbc操作数据库

    一 概述 为了持久化 方便管理数据 出现了mysql sqlserver等多种数据库 我们可以直接这些数据库中使用sql语言增删改查管理数据 但是对于业务人员来说不懂sql 没法通过sql直接在数据库操作 所以我们要通过程序提供对数据库的操
  • 用Qt实现QQ好友列表界面伸缩功能(完全一模一样)(伸展和收缩、抽屉效果、类似树形控件)(鼠标划过QSS效果)

    本文主要总结用Qt的自定义按钮和QWidget界面实现QQ好友列表的界面伸展和收缩功能 以及鼠标滑过 鼠标单击的QSS样式表效果 全文分为两大部分 分别是原理讲解和效果实现 抽缩界面效果图 源代码下载地址 https download cs
  • 增量测试:自顶向下测试&自底向上测试

    本博客主要内容 自顶向下测试和自底向上测试的优缺点 软件开发周期流程 不同的测试方法针对不同的测试阶段 一 自顶向下测试 优点 1 如果主要的缺陷发生在程序的顶层将非常有利 2 一旦引入I O功能 提交测试或更容易 3 早期的程序框架可以进

随机推荐

  • VS2013,MFC,在视图类里添加鼠标左键响应函数OnLButtonDown

    以CVoronoi2D为例子 点击类视图的View 右击选择类向导 选择WM LBUTTONDOWN 鼠标左击响应函数 然后点击添加处理程序 代码会自动生成一个响应函数 如图 如果对您有帮助 可以评论一下 谢谢
  • 失败的人生图片_人到中年,做事失败了,很可能是遇到了以下五种情况

    人至中年 也到了迈入成功大门的时刻 但并非每个人都能在中年获得成功 相反 有不少人却在中年的时候失败 人至中年面临失败 其实原因有很多 但大多数情况下 可能是遇到了以下五种情况 究竟有哪五种情况呢 如果您想知道 就让小编来为您揭秘 本文所有
  • hash与map的区别联系应用

    一 hashtable原理 哈希表又名散列表 其主要目的是用于解决数据的快速定位问题 考虑如下一个场景 一列键值对数据 存储在一个table中 如何通过数据的关键字快速查找相应值呢 不要告诉我一个个拿出来比较key啊 呵呵 大家都知道 在所
  • 设计模式GitHub找的好东西

    https github com DovAmir awesome design patterns https github com JakubVojvoda design patterns cpp https github com Wale
  • Appium自动化框架从0到1之 业务模块封装(登录页面业务操作)

    我们这次来封装登录页面业务操作 在上代码之前 我们先了解一下登录场景 用户名 密码 小鱼1号 fish1 小鱼2号 fish2 小鱼3号 fish3 然后 我们在登录的时候 会进行一下几个操作 我们先输入账号 密码 点击 登录按钮 登录后
  • 【UE4】TSubclassOf的使用

    TSubclassOf TSubclassOf 是提供 UClass 类型安全性的模板类 例如您在创建一个投射物类 允许设计者指定伤害类型 您可只创建一个 UClass 类型的 UPROPERTY 让设计者指定派生自 UDamageType
  • phpcms(1)phpcms V9 MVC模式 与 URL访问解析(转)

    1 URL访问解析 观察访问网页时的网址 可以得出模块访问方法 如下示例 http www abcd com cn phpcms index php m content c index a show id 1 关于此URL解析如下 m co
  • Android Studio 链接外部项目的Module

    Android Studio 链接外部项目的Module 前言 引用外部Module 操作教程 最后我还有一句话要说 两情若是久长时 又岂在 朝朝暮暮 前言 有的时候自己写的Module要在多个项目同步使用 但是使用Android Stud
  • 九.修改AD用户属性-账户-账户选项

    LDAP修改ad用户账户选项 这里只提供了两种常用的 更多的请参考专栏 帮助类中的枚举 region 修改用户选项
  • 安装C/C++插件一直显示正在安装如何处理?

    有一位小伙伴在看我的一篇文章 VScode使用教程 菜鸟版 本文链接 VScode使用教程 菜鸟版 中二病的易哥哥的博客 CSDN博客问我安装C C 插件一直显示正在安装如何处理 因为我实在没有遇到过这种情况 我唯一可以想得到的办法时重启V
  • 关于IntelliJ IDEA找不到getServletContext()的问题

    在Eclipse里面使用Tomcat7 0以上 HttpServletRequest request的getServletContext完全没有问题 但是在IntelliJ Idea里面却没有提示 而且getRealPath 还显示过期 网
  • 深入理解spring生命周期与BeanPostProcessor的实现原理

    上面两篇文章分别介绍了spring生命周期中初始化和销毁的几种方式以及统一后置BeanPostProcessor接口的使用 可以点击以下链接查看 三分钟了解spring bean生命周期之初始化和销毁的三种方式 一分钟学会spring be
  • (测试有效)Windows10开机自动打开空白word、excel、PowerPoint问题的解决办法

    开始 gt 设置 gt 账户 gt 在左边找到 登录选项 gt 往下拉到 隐私 标题 找到下图设置 并关闭这个开关 这样以后开机就不会自动打开Office的空白文档了
  • 【Android取证篇】一键分析APK利器-APK Messenger

    APK Messenger篇 一键分析APK应用信息 对于只想了解基础APK信息的 可节约宝贵时间 suy 文章目录 APK Messenger篇 一 软件特色 二 APK分析 1 APK基础信息 2 权限信息 2 签名信息 3 其他信息
  • 【python】‘DataFrame‘ object has no attribute ‘as_matrix‘

    问题 解决 网上的文章可能比较老 使用的是老版本的pandas 目前新版本的pandas这个方法没有了 更换成了别的实现方式 data as matrix 更改为 data iloc values
  • Vue 项目 build 流程解析(webpack工具解析)

    Vue 项目 build 流程解析 webpack工具解析 注 本篇文章解析框架为 vue2 0 本篇文章通过解析简单的项目打包步骤试着去了解我们的 Vue 项目是怎么打包的 build js 干了什么 首先我们贴上 build js 代码
  • git重新生成ssh密钥

    当更换电脑之后需要重新获取git密钥并配置 下面是gitee重新生成ssh密钥的方法 先删除之前的ssh公钥 删除之后开始重新生成 ssh keygen t rsa C 邮箱地址 然后跟着步骤进行三次回车 之后开始获取生成的ssh公钥 ca
  • Python 3 入门与进阶:探索编程世界的奇妙之旅

    Python 3 入门与进阶 探索编程世界的奇妙之旅 Python 是一门功能强大且易于学习的编程语言 它在各个领域都有广泛的应用 无论你是初学者还是有经验的开发者 掌握 Python 编程技能都将为你打开一扇通往编程世界的大门 本文将为你
  • Python开发是面向过程、函数还是对象?

    面向过程和面向对象是一种编程思想 那么Python开发是面向过程 面向函数还是面向对象呢 这里小编告诉大家 Python既支持面向对象 也支持面向过程 尽管 Python 是一种解释型语言 但它从一开始就是一种面向对象的语言 在 Pytho
  • 剑指offer 学习笔记 复杂链表的复制

    分治法 把分解后的小问题各个解决 然后把小问题的解决方案结合起来解决大问题 面试题35 复杂链表的复制 请实现函数ComplexListNode Clone ComplexListNode pHead 复制一个复杂链表 在复杂链表中 每个节