《算法之美》の链表问题の从链表中删除节点

C语言 单链表(带头节点)的增删改查 c语言 数据结构 单链表(带头节点)の相关函数的定义 提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录前言一、代码1.单链表的定义2.相关函数总结 前言 单链表(带头节点)的相关操作 提示:以下代码均基于带头结点的单链表 一、代码 1.单链表的定义 代码如下(示例): typedef struct LNode{//定义单链表节点类型 int data; struct LNode *next; }LNode,*LinkList; //LNode *L 等价与 LinkList L //节点 阅读详情

问题:假设一个没有头指针的单链表。一个指针指向此单链表中间的一个节点(既不是第一个,也不是最后一个节点),请将该节点从单链表中删除。链表结点定义如下:

struct ListNode

{

         int       m_nKey;

         ListNode* m_pNext;

};

解答:假设给定的指针为pCurrent,ListNode* pNext = pCurrent->m_pNext;

由题意知,pCurrent指向链表的某一个中间节点,因此pCurrent->m_pNext != NULL。

要删除pCurrent指向的节点B很简单,但必须将节点B前后两个节点A和C连接起来,但是单链表节点没有头指针,因此无法追溯到A,也就无法将A和C相连了。

无法删除节点B,但我们可以删除B的后继节点C,并通过pCurrent->m_pNext = pCurrent->m_pNext->m_pNext重新将链表连接起来,而唯一丢失的是节点C的数据项m_nKey。因此,我们只需要将节点C的数据项取代节点B的数据项,然后将真正指向节点C的指针删除即可实现将节点B“删除”!!关键代码如下:

pCurrent->m_pNext = pNext->m_pNext;

pCurrent->m_nKey = pNext->m_nKey;

delete pNext;

 

完整代码如下:

#include <iostream>

#include <assert.h>

 

struct ListNode

{

    int m_nKey;

    ListNode* m_pNext;      

};

 

//参数必须是指向指针的指针,因为要修改指针

//所以要考虑“参数本地拷贝”问题

void InitList(ListNode** pList)

{

     *pList = (ListNode*)malloc(sizeof(ListNode));

     (*pList)->m_pNext = NULL;

}

 

//第一个参数不必是指向指针的指针,因为函数内只修改指针指向的数据

//所以不用担心“参数本地拷贝”问题

void InsertList(ListNode* pList, int data)

{

    ListNode* pNewNode = (ListNode*)malloc(sizeof(ListNode));

    pNewNode->m_nKey = data;

    pNewNode->m_pNext = pList->m_pNext;

    pList->m_pNext = pNewNode;

}

 

//核心算法

void DeleteRandomNode(ListNode* pCurrent)

{

    assert(pCurrent != NULL);

    ListNode* pNext = pCurrent->m_pNext;

    if(pNext != NULL)

    {

        pCurrent->m_pNext = pNext->m_pNext;

        pCurrent->m_nKey = pNext->m_nKey;

        delete pNext;      

                   pNext = NULL;

    }    

}

 

//打印链表元素

void PrintListNormally(ListNode* pListHead)

{

    ListNode* pTempNode = pListHead->m_pNext;

    while(pTempNode != NULL)

    {

        std::cout<<pTempNode->m_nKey<<std::endl;

        pTempNode = pTempNode->m_pNext;            

    }    

}

 

 

int main()

{

    ListNode* pHead = NULL;

    InitList(&pHead);

    for(int i=9; i>=0; i--)

    {

        InsertList(pHead,i);       

    }

   

    PrintListNormally(pHead);

   

    ListNode* pNext = pHead->m_pNext->m_pNext; //删除第个元素

   

    DeleteRandomNode(pNext);

   

    PrintListNormally(pHead);

   

    system("pause");

    return 0;   

}

 

=======================================================

扩展题目:给定链表的头指针和一个结点指针,在O(1)时间删除该结点。链表结点的定义如下:

struct ListNode

{

         int        m_nKey;

         ListNode*  m_pNext;

};

函数的声明如下:

void DeleteNode(ListNode* pListHead, ListNode* pToBeDeleted);

分析:这是一道广为流传的Google面试题,能有效考察我们的编程基本功,还能考察我们的反应速度,更重要的是,还能考察我们对时间复杂度的理解。

在链表中删除一个结点,最常规的做法是从链表的头结点开始,顺序查找要删除的结点,找到之后再删除。由于需要顺序查找,时间复杂度自然就是O(n) 了。

我们之所以需要从头结点开始查找要删除的结点,是因为我们需要得到要删除的结点的前面一个结点。我们试着换一种思路。我们可以从给定的结点得到它的下一个结点。这个时候我们实际删除的是它的下一个结点,由于我们已经得到实际删除的结点的前面一个结点,因此完全是可以实现的。当然,在删除之前,我们需要需要把给定的结点的下一个结点的数据拷贝到给定的结点中。此时,时间复杂度为O(1)。

上面的思路还有一个问题:如果删除的结点位于链表的尾部,没有下一个结点,怎么办?我们仍然从链表的头结点开始,顺便遍历得到给定结点的前序结点,并完成删除操作。这个时候时间复杂度是O(n)。

那题目要求我们需要在O(1)时间完成删除操作,我们的算法是不是不符合要求?实际上,假设链表总共有n个结点,我们的算法在n-1总情况下时间复杂度是O(1),只有当给定的结点处于链表末尾的时候,时间复杂度为O(n)。那么平均时间复杂度[(n-1)*O(1)+O(n)]/n,仍然为O(1)。

基于前面的分析,我们不难写出下面的代码。

核心参考代码:

//核心算法

//pListHead是链表头指针,pCurrent是要删除的节点指针

void DeleteRandomNode(ListNode* pListHead, ListNode* pCurrent)

{

    assert(pCurrent != NULL || pListHead != NULL);

   

    if(pCurrent->m_pNext != NULL) //要删除的节点不是最后一个节点

    {

        ListNode* pNext = pCurrent->m_pNext;

        pCurrent->m_nKey = pNext->m_nKey;

        pCurrent->m_pNext = pNext->m_pNext;

        delete pNext;

        pNext = NULL;                   

    }

    else //要删除的节点是链表中最后一个节点

    {

        ListNode* pNode = pListHead;

        while(pNode->m_pNext != pCurrent) //得到要删除节点的前继节点

        {

            pNode = pNode->m_pNext;                    

        }

        pNode->m_pNext = NULL;

        delete pCurrent;

        pCurrent = NULL;

    }

}

完整的代码只需替换第一题的相应函数,并在main函数调用中做相应修改即可。

 

值得注意的是,为了让代码看起来简洁一些,上面的代码基于两个假设:(1)给定的结点的确在链表中;(2)给定的要删除的结点不是链表的头结点。不考虑第一个假设对代码的鲁棒性是有影响的。至于第二个假设,当整个列表只有一个结点时,代码会有问题。但这个假设不算很过分,因为在有些链表的实现中,会创建一个虚拟的链表头,并不是一个实际的链表结点。这样要删除的结点就不可能是链表的头结点了。当然,在面试中,我们可以把这些假设和面试官交流。这样,面试官还是会觉得我们考虑问题很周到的。

 

 

 

给定链表的头指针和一个结点指针,在O(1)时间删除该结点 有详细的讲解和代码,而且代码不是伪码,方便大家学习。 立即下载

相关推荐

线性回归LinearRegression

对视频中线性回归算法的相关内容进行总结与记录 文章目录一、理论基础二、实例2.1 一元线性回归2.1.1 推导过程2.1.2 代码实现(基于numpy)2.1.3 代码实现(基于sklearn)2.2 多元线性回归2.2.1 代码实现(基于numpy)2.2.2 代码实现(基于sklearn) 一、理论基础 回归 通常是指利用某个函数,尽可能把数据样本点“串”在一起,用于描述输入变量和输出变量间的变化关系 线性回归 特点:用来把数据“串”起来的那个函数是线性的 (一元线性回归(一个自变量) &a.

邱邱邱的博客 1068

在O(1)时间删除链表节点

题目:给定单链表的头指针和一个节点指针,定义一个函数在O(1)时间删除该节点 分析: 1.在寻常的单链表中删除一个节点,使用的最多的做法就是从链表的头结点开始,遍历链表,找到要删除的节点,并在链表中删除该节点,比如我要删除链表中节点b,我在遍历的时候找到了b节点的上一节点a,那么我可以把a节点的指针指向b节点的下一节点,这样就完成了对b节点的删除。但是需求规定的是我们完成删除操作时,时间复杂度

kuangsonghan的博客 445

滚动轴承振动信号特征解析与故障诊断模型构建

本文深入解析了滚动轴承振动信号的三大来源与特征,并详细阐述了从时域、频域到时频域的特征提取方法。通过构建基于机器学习(如随机森林、SVM)或深度学习的故障诊断模型,结合实战代码示例,为设备维护工程师提供了一套从信号分析到智能诊断的完整解决方案,旨在实现精准的故障识别与预测性维护。

qsc901234567的博客 368

深度解析带头节点单链表的增删改查与销毁链表等操作(含算法编写步骤,有完整代码)

还不会带头节点单链表的增删改查与销毁链表等操作吗,一篇带你搞定。不仅仅有算法思路带你一步步去理解,还有完整代码等你去使用(含算法编写步骤,有完整代码)

勾栏听曲_0的博客 4106

机器人控制入门

机器人控制入门 对于工科领域来说,脱离实践的学习都是肤浅的,对于控制这种强调经验的技术更是如此。如果去问一个程序员怎么学习一块技术,他必然让你去多编程。机器人领域也是。如果想把基本功打扎实,那么实践更是必不可少了。 机器人控制有三个元素:控制器(算法),执行器(电机),传感器; 控制的本质就是将规划系统的指令作为输入信息,将传感器探测得到的状态信息和导航系统的定位

彼岸花 1万+

Unity之如何实现TTS文本转语音(真人发音)

微软Azure TTS 文本转语音 服务 将文本转换为逼真的语音的语音服务功能 支持个各国语言,各种中文各种方言,支持不同音色男音女音 支持离线和在线版本

TxNet 5479

在O(1)时间删除链表结点

题目描述 给定单向链表的头指针和一个节点指针,定义一个函数在O(1)时间删除该节点。 编程思路 首先要分析要删除的结点所在的位置,一共有三种可能:头结点(链表只有一个结点),尾结点,中间结点。 如果链表只有一个结点,则删除链表的头结点(也是尾结点),就需要把链表的头结点设置为NULL。 如果链表有多个结点,要删除尾结点时,就需要从链表的头结点开始,顺序遍历得到该结点的

土豆萝卜会发光的博客 593

梯度提升决策树GBDT原理与代码实例讲解

梯度提升决策树GBDT原理与代码实例讲解 1.背景介绍 1.1 机器学习发展历程 机器学习是人工智能领域的一个重要分支,旨在使计算机具备学习和改进自身性能的能力。随着大数据时代的到来,海量的数据为机器学习算法提供了广阔的应用空间。在

AI天才研究院 1084

【剑指offer】给定单向链表的头指针和一个结点指针,定义一个函数在 O(1) 时间删除该结点。

正常的做法肯定是在 O(N)时间内删除节点。而这么过分的要求,显然是通过“重新赋值”才能做到。 比如要删除节点 a,那么就将 a.next 的 value 和 next 赋值给节点 a,然后删除 a.next。 表面“看起来”像是删除了节点 a,其实是将其后节点的信息转移到了它自己身上。 除此之外,对于最后一个节点,还是要退化成 $O(N)$ 的复杂度。而整体分析一下复杂度: $O(T)...

包磊磊的博客 1375

k8s配置habor作为镜像地址之后,如果harbor中没有镜像,如何设置自动从阿里云官方镜像仓库下载

通过 Harbor 的远端仓库可以集中管理。如果你在上游设置了 image prefix 为 library,那么对外访问 harbor.your-domain.com/library/nginx:latest 就会被 Harbor 去上游拉取 registry.cn-hangzhou.aliyuncs.com/library/nginx:latest 并缓存。- 将集群中的镜像地址改为 Harbor 的域名,比如 harbor.your-domain.com/library/nginx:latest。

极客栈 422

EEMD算法原理及应用

本博客主要概括EEMD算法的背景、原理以及应用,利用EEEMD对信号进行分解,然后选择有用的IMF分量进行叠加以完成信号的重构,从而实现对信号降噪。本文配套的源代码见文中链接,可供大家免费下载。

总结论文中的算法并给出论文的复现代码 4万+

删除单链表の指定节点

删除单链表的指定节点删除节点基本思想代码复杂度分析 删除节点 3 -> 2 -> 0 -> 3 -> 4 -> 5 假定删除3这个节点,删除后的链表:2 -> 0 -> 4 -> 5 基本思想 存在删除头节点的情况 先看是否需要删除头节点以及要删除几个头节点 头节点删除完成后,当前节点即为链表的头节点,且作为返回 遍历单链表 用 cur 表示当前节点,用 pre 表示当前节点的前一个节点,用 num 表示目标值 当 cur值 = num时,则将

qq_43337680的博客 916

23、OpenMP常见用例与标准循环级示例详解

本文详细介绍了OpenMP的三种典型用例:循环级OpenMP、高级OpenMP和MPI+OpenMP,适用于不同层次的并行需求。重点讲解了循环级OpenMP在向量加法、流三元组和模板计算中的应用示例,并探讨了首次触摸优化、线程绑定、NUMA架构影响等关键性能优化策略。通过实际代码演示和性能分析,帮助开发者快速掌握OpenMP的基础并行化方法,并为后续高性能优化提供指导。

fanta的博客 85

《算法之美》の链表问题の获得链表中倒数第m个元素

问题: 给定一个单向链表,设计一个时间优化并且空间优化的算法,找出该链表的倒数第m个元素。实现您的算法,注意处理相关的出错情况。m定义为当m=0时,返回链表最后一个元素。   解答: 这是一个难题,因为单向链表只能正向遍历,这个问题需要根据元素与链表尾的相对位置来找出该元素,但是当发现链表尾时,没有简单的办法回溯到倒数第m个元素。   我们需要的是倒数第m个元素

ASCE1885 2676

efm32 入门.pdf

本PDF共计22页,介绍的是操作驱动API,下载与否自己斟酌。

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

《算法之美》の链表问题の记住头元素+删除元素

记住头元素:单向链表的头元素必须至始至终要记住,否则链表将会在内存中丢失。这意味着在链表第一个元素之前插入新元素或删除链表第一个元素时,指向链表头的指针或引用必须更新。在C/C++中,很容易因误用指针而犯错误,如下面代码,它在链表的前面插入一个元素:bool insertInFront(IntElement *head, int data){         IntElemen

ASCE1885 1654

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

《剑指Offer》--- 数据结构の链表篇

✨知识回顾✨ 小伙伴还记得 head,node,val,prev 和 next,以及链表的一些基础操作嘛?如果忘记了,那就一起来回顾一下吧🌞 👉数据结构---链表 面试题6:从尾到头打印链表 题目: 输入一个链表的头节点,从尾到头反过来返回每个节点的值(用数组返回) 示例: 输入:head = [2,5,7] 输出:[7,5,2] 链接:剑指 Offer 06 方法一:题目要求用数组返回,我们只需要创建一个链表节点数大小的数组,然后在数组中从后往前输入链表的结点打印即可。 .

Lucky_mzc的博客 1556

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

删除单链表中间节点

题目:(先思考,再往下看) 从无头链表中删除节点。假设有一个没有头指针的单链表。一个指针指向此链表中间的一个节点(不是第一个,也不是最后一个节点),请将该节点从单链表中删除---《编程之美》 参考:单链表反转《算法之美》の链表问题の从链表中删除节点 代码待续 。。。。。。 转载于:https://www.cnblogs.com/kira2will/p/4111323.ht...

weixin_34380948的博客 244

《算法之美》の链表问题のremoveHead中的缺陷

问题:发现并修复下面C/C++函数中的缺陷,该函数是想从一个单向链表中删除头元素。void removeHead(Node *head){         delete head;         head = head->next;} 解答:缺陷发现策略:对于拿到的任何函数,考虑下面4个常见的问题:1)检查数据是否正确地进入了该函数;2)检查函数的每一行是

ASCE1885 1857

【亡羊补牢】挑战数据结构与算法 第60期 LeetCode 剑指 Offer 18. 删除链表的节点(链表)

仰望星空的人,不应该被嘲笑 题目描述 给定单向链表的头指针和一个要删除的节点的值,定义一个函数删除该节点。 返回删除后的链表的头节点。 注意:此题对比原题有改动 示例 1: 输入: head = [4,5,1,9], val = 5 输出: [4,1,9] 解释: 给定你链表中值为 5 的第二个节点,那么在调用了你的函数之后,该链表应变为 4 -> 1 -> 9. 示例 2: 输入: head = [4,5,1,9], val = 1 输出: [4,5,9] 解释: 给定你链表中值为 1..

超逸の学习技术博客 459

【亡羊补牢】挑战数据结构与算法 第59期 LeetCode 19. 删除链表的倒数第N个节点(链表)

仰望星空的人,不应该被嘲笑 题目描述 给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点。 示例: 给定一个链表: 1->2->3->4->5, 和 n = 2. 当删除了倒数第二个节点后,链表变为 1->2->3->5. 说明: 给定的 n 保证是有效的。 进阶: 你能尝试使用一趟扫描实现吗? 来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/remove-nth-node-from-e..

超逸の学习技术博客 522
上一篇: 《算法之美》の链表问题の从尾到头输出链表
下一篇: 《算法之美》の链表问题の两链表相交问题
ACE1985
博客等级 码龄17年 1307粉丝 308原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值