C#刷剑指Offer | 在O(1)时间删除链表节点

【C#刷题作者 / Edison Zhou


我们来用之前学到的数据结构知识来刷《剑指Offer》的一些核心题目(精选了其中30+道题目),希望对你有帮助!本文题目为:在O(1)时间删除链表节点。

1题目介绍

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

《剑指Offer》一书中使用的C/C++来定义节点,我们这里使用C#来定义节点:

public class Node<T>
{// 数据域public T Item { get; set; }// 指针域public Node<T> Next { get; set; }public Node()
{}public Node(T item)
{this.Item = item;}
}

我们最终要实现的DeleteNode方法定义如下:

public static void DeleteNode(Node<int> headNode, Node<int> deleteNode)
{
}

2解法1:常规思路

在单向链表中删除一个结点,最常规的做法无疑是从链表的头结点开始,顺序遍历查找要删除的结点,并在链表中删除该结点。这种思路由于需要顺序查找,时间复杂度自然就是O(n)

3解法2:正确思路

是不是一定需要得到被删除的结点的前一个结点呢?答案是否定的。

我们可以很方便地得到要删除的结点的下一个结点。因此,我们可以把下一个结点的内容复制到需要删除的结点上覆盖原有的内容,再把下一个结点删除,就相当于把当前需要删除的结点删除了。

但是,还有两个特殊情况需要进行考虑:

(1)如果要删除的结点位于链表的尾部,那么它就没有下一个结点:

此时我们仍然从链表的头结点开始,顺序遍历得到该结点的前序结点,并完成删除操作,这仍然属于O(n)时间的操作。

(2)如果链表中只有一个结点,而我们又要删除链表的头结点(也是尾结点):

此时我们在删除结点之后,还需要把链表的头结点设置为NULL。

最后,通过综合最坏情况(尾节点需要顺序查找,1次)和最好情况(n-1次),因此平均时间复杂度为O(1)

需要注意的是:受到O(1)时间的限制,我们不得不把确保结点在链表中的责任推给了方法DeleteNode的调用者

下面是DeleteNode方法的C#代码实现:

public static void DeleteNode(Node<int> headNode, Node<int> deleteNode)
{if (headNode == null || deleteNode == null){return;}if (deleteNode.Next != null) // 链表有多个节点,要删除的不是尾节点:O(1)时间{Node<int> tempNode = deleteNode.Next;deleteNode.Item = tempNode.Item;deleteNode.Next = tempNode.Next;tempNode = null;}else if (headNode == deleteNode) // 链表只有一个结点,删除头结点(也是尾结点):O(1)时间{deleteNode = null;headNode = null;}else // 链表有多个节点,要删除的是尾节点:O(n)时间{Node<int> tempNode = headNode;while(tempNode.Next != deleteNode){tempNode = tempNode.Next;}tempNode.Next = null;deleteNode = null;}
}

4单元测试

老规矩,仍然需要为我们的代码写单元测试,这也是一个好习惯哦!

首先,为了后面测试验证方便,这里我们先封装一个测试辅助方法GetPrintNodes用于对比实际值与期望值是否一致:

public static string GetPrintNodes(Node<int> headNode)
{if (headNode == null){return string.Empty;}StringBuilder sbNodes = new StringBuilder();while(headNode != null){sbNodes.Append(headNode.Item);headNode = headNode.Next;}return sbNodes.ToString();
}

其次,测试代码:

// 链表中有多个结点,删除中间的结点
[TestMethod]
public void DeleteNodeTest1()
{Node<int> head1 = new Node<int>(1);Node<int> head2 = new Node<int>(2);Node<int> head3 = new Node<int>(3);Node<int> head4 = new Node<int>(4);Node<int> head5 = new Node<int>(5);head1.Next = head2;head2.Next = head3;head3.Next = head4;head4.Next = head5;Program.DeleteNode(head1, head3);Assert.AreEqual(Program.GetPrintNodes(head1),"1245");
}// 链表中有多个结点,删除尾结点
[TestMethod]
public void DeleteNodeTest2()
{Node<int> head1 = new Node<int>(1);Node<int> head2 = new Node<int>(2);Node<int> head3 = new Node<int>(3);Node<int> head4 = new Node<int>(4);Node<int> head5 = new Node<int>(5);head1.Next = head2;head2.Next = head3;head3.Next = head4;head4.Next = head5;Program.DeleteNode(head1, head5);Assert.AreEqual(Program.GetPrintNodes(head1), "1234");
}// 链表中有多个结点,删除头结点
[TestMethod]
public void DeleteNodeTest3()
{Node<int> head1 = new Node<int>(1);Node<int> head2 = new Node<int>(2);Node<int> head3 = new Node<int>(3);Node<int> head4 = new Node<int>(4);Node<int> head5 = new Node<int>(5);head1.Next = head2;head2.Next = head3;head3.Next = head4;head4.Next = head5;Program.DeleteNode(head1, head1);Assert.AreEqual(Program.GetPrintNodes(head1), "2345");
}// 链表中只有一个结点,删除头结点
[TestMethod]
public void DeleteNodeTest4()
{Node<int> head1 = new Node<int>(1);Program.DeleteNode(head1, head1);head1 = null;Assert.AreEqual(Program.GetPrintNodes(head1), "");
}// 链表为空
[TestMethod]
public void DeleteNodeTest5()
{Program.DeleteNode(null, null);Assert.AreEqual(Program.GetPrintNodes(null), "");
}

最后,测试结果如下:

代码覆盖率如下:

Ref参考资料

何海涛,《剑指Offer》

后台回复:剑指offer,即可获得pdf下载链接哟!

专注于开发技术与个人成长分享,

对你有用的公众号!

????点击获取文章源码

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/308352.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

oracle计算最大与最小之间数,oracle 分析函数

oracle分析函数&#xff0c;主要五大类一等级函数:row_number();DENSE_RANK();Rank()主要就是遇到相同排名时的区别&#xff0c;查询各部门职位级别最高的2个人select * from (select rybm,rymc,zzjgid,zwdj,row_number() over(partition by zzjgid order by zwjb desc ) rw f…

7-44 基于词频的文件相似度 (30 分)(思路加详解+set容器简便做法)兄弟们冲呀呀呀呀呀 今天你AC了吗

一&#xff1a;题目 实现一种简单原始的文件相似度计算&#xff0c;即以两文件的公共词汇占总词汇的比例来定义相似度。为简化问题&#xff0c;这里不考虑中文&#xff08;因为分词太难了&#xff09;&#xff0c;只考虑长度不小于3、且不超过10的英文单词&#xff0c;长度超过…

堪称艺术品级的应用开发框架,Abp有望超越Spring?

ABP是ASP.NET Boilerplate的简称&#xff0c;是一个开源且文档友好的应用程序框架。ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应用程序的新起点&#xff0c;它旨在成为一个通用的WEB应用程序框架和项目模板。ASP.NET Boilerplate 基于DDD的经典分层架构思想&a…

7-45 航空公司VIP客户查询 (25 分)(思路+详解+map用法解释+超时解决)兄弟们来呀冲压呀呀呀呀

一&#xff1a;题目 不少航空公司都会提供优惠的会员服务&#xff0c;当某顾客飞行里程累积达到一定数量后&#xff0c;可以使用里程积分直接兑换奖励机票或奖励升舱等服务。现给定某航空公司全体会员的飞行记录&#xff0c;要求实现根据身份证号码快速查询会员里程积分的功能…

linux sed 写文件,使用sed对文件进行操作

使用sed对文件进行操作[日期&#xff1a;2012-12-07]来源&#xff1a;Linux社区作者&#xff1a;bobo12082119[字体&#xff1a;大 中 小]一.附加文本使用a\在指定行后面附加1行或多行&#xff1b;若不指定放置的位置&#xff0c;则默认放到每一行的后面。附加文本时&#xff0…

修复被破坏的 vs 工程设置(续)

缘起 在上一篇文章——《修复被破坏的 vs 工程设置》中&#xff0c;我分享了修复被破坏的 vs 工程设置的实战。本以为圆满解决了问题&#xff0c;没想到另有玄机。所以又来分享一篇刨根问底的文章。查看文件 打开 Microsoft.Cpp.Win32.user.props 一看&#xff0c;里面并没有任…

linux find查找文件然后删除,linu查找find命令及删除7天前的文件

在/ l o g s目录中查找更改时间在7日以前的文件并删除它们&#xff1a; $ find logs/ -type f -mtime 7 -exec rm -f {} \; 在/ l o g s目录中查找更改时间在7日以内的文件并删除它们&#xff1a; $ find logs/ -type f -mtime -7 -exec rm -f {} \; 然后加入的计划任务中每日执…

7-46 新浪微博热门话题 (30 分)(思路+详解+set + map)pta逐个点过的 来呀兄弟们

一 &#xff1a;题目 新浪微博可以在发言中嵌入“话题”&#xff0c;即将发言中的话题文字写在一对“#”之间&#xff0c;就可以生成话题链接&#xff0c;点击链接可以看到有多少人在跟自己讨论相同或者相似的话题。新浪微博还会随时更新热门话题列表&#xff0c;并将最热门的…

工作十余年,还是一直被问 委托和事件 有什么区别? 真是够了

一&#xff1a;背景1. 讲故事前几天公司一个妹子问我&#xff0c;事件和委托有什么区别&#xff1f;先由衷感叹一下&#xff0c;编码十余年&#xff0c;年轻的时候常被面试官问起&#xff0c;现在年长了&#xff0c;却被后辈们时常问候&#xff0c;看样子逃离编码生涯之前是跑不…

7-47 打印选课学生名单 (25 分)(两种做法)(思路加详解+map+vector做法+最后一个点超时解决)+兄弟们冲丫丫

一&#xff1a;题目 假设全校有最多40000名学生和最多2500门课程。现给出每个学生的选课清单&#xff0c;要求输出每门课的选课学生名单。 输入格式: 输入的第一行是两个正整数&#xff1a;N&#xff08;≤40000&#xff09;&#xff0c;为全校学生总数&#xff1b;K&#xf…

linux取设备分辨率,linux 获取系统屏幕分辨率

三步将Node应用部署到Heroku上Heroku是一个提供快速部署服务的云平台.支持Node,Ruby,Java,PHP,Python,Go多种语言,今天体验了下,简直不要太爽.下面简单的介绍一下. 首先还是要注册一个账号:https: ...jQuery操作Table tr td常用的方法虽然现在DIVCSS进行页的布局大行其道,但是很…

SwaggerUI看烦了,IGeekFan.AspNetCore.Knife4jUI 帮你换个新皮肤

背景好像是上周四&#xff0c;看到微信群有人说java有轮子swagger-bootstrap-ui&#xff0c;而c#&#xff0c;就是找不到。于是我一看&#xff0c;就说大话&#xff1a;“这个只是一套UI&#xff0c;他这个有开源地址么”被at说:你试试...当天晚上就把swagger-ui, Knife4j,Swas…

7-48 银行排队问题之单窗口“夹塞”版 (30 分)(思路和详解+map做法)来呀Baby!

一&#xff1a;题目 排队“夹塞”是引起大家强烈不满的行为&#xff0c;但是这种现象时常存在。在银行的单窗口排队问题中&#xff0c;假设银行只有1个窗口提供服务&#xff0c;所有顾客按到达时间排成一条长龙。当窗口空闲时&#xff0c;下一位顾客即去该窗口处理事务。此时如…

linux 启动程序 绑定id,linux如何根据进程ID查找启动程序的路径

昨天遇到一个问题,背景是这样的:我们工作环境不正常,使用ps命令查看,发现有程序A的两个进程状态一个是状态,一个是正常运行.由于该程序A是个通用服务程序,被拷贝成多份,分发到不同的目录中单独启动,只有各自配置不同而已,因此想知道是哪个目录的A程序处于僵死状态.正常运行中的…

7-49 打印学生选课清单 (25 分)(思路+详解+map做法(一对多)+超时解决)Come baby!

一&#xff1a;题目 假设全校有最多40000名学生和最多2500门课程。现给出每门课的选课学生名单&#xff0c;要求输出每个前来查询的学生的选课清单。 输入格式: 输入的第一行是两个正整数&#xff1a;N&#xff08;≤40000&#xff09;&#xff0c;为前来查询课表的学生总数&…

记一次批量处理数据库中的敏感信息

前言 对于一些敏感数据&#xff0c;往往会对其加密后再入库&#xff0c;这个是对数据安全性的一个最为简单的措施。最常见的莫过于手机号码和身份证号了&#xff0c;相信还是有不少公司对这些敏感信息是明文存储的。万一被别人发现系统漏洞&#xff0c;或者是被拖库&#xff0c…

修改linux路径,科学网-修改linux终端路径颜色-胡涛的博文

PuTTY或者Terminal路径默认颜色非常难看&#xff0c;如何修改&#xff1f;第一步&#xff0c;配置文件&#xff1a;命令行&#xff1a;dircolors -p > ~/.dircolors第二步&#xff0c;修改文件&#xff1a;使用vi打开~/.dircolors 文件&#xff0c;找到这一行&#xff1a; D…

7-50 畅通工程之局部最小花费问题 (35 分)(思路加详解)来呀兄弟们冲呀呀呀呀呀呀呀

一&#xff1a;题目 某地区经过对城镇交通状况的调查&#xff0c;得到现有城镇间快速道路的统计数据&#xff0c;并提出“畅通工程”的目标&#xff1a;使整个地区任何两个城镇间都可以实现快速交通&#xff08;但不一定有直接的快速道路相连&#xff0c;只要互相间接通过快速…

十多位全球技术专家,为你献上近十个小时的.Net微服务介绍

.Net Conf: Focus on Microservices 是 .Net Conf 社区在 2020 年 7 月 30 日举办的线上分享活动。整个活动视频长达近 10 个小时。今天我们来看看都发生了什么。章节汇总本次分享由十多位来自全球的资深技术专家在线分享&#xff0c;涵盖了当前 .Net 在微服务领域的利器。包括…

向linux内核增加新的系统调用,为linux内核添加新的系统调用

为linux内核添加新的系统调用作者&#xff1a;李志勇更多精彩&#xff1a;更多精彩&#xff1a;开发平台&#xff1a;x86 ubuntu目标平台&#xff1a;S3C6410linux3.4.4一、 打开内核源码目录下arch/arm/kernel/calls.S文件&#xff0c;在389行添加&#xff1a;/*378 */CALL…