一、题目:在O(1)时间删除链表结点

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

  原文采用的是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)

2.2 正确思路

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

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

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

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

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

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

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

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

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

三、解决问题

3.1 代码实现

    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;
}
}

3.2 单元测试

  (1)封装返回结果

  该方法作为单元测试的对比方法,主要用来对比实际值与期望值是否一致:

    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();
}

  (2)测试用例

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

  测试通过结果:

  (3)代码覆盖率

作者:周旭龙

出处:http://edisonchou.cnblogs.com

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。

剑指Offer面试题:12.在O(1)时间删除链表结点的更多相关文章

  1. 剑指Offer:面试题13——在O(1)时间删除链表结点

    问题描述: 给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点.链表结点与函数的定义如下: public class ListNode{ int value; ListNode ...

  2. 【剑指Offer面试题】 九度OJ1518:反转链表

    与其非常快写出一段漏洞百出的代码,倒不如细致分析再写出鲁棒的代码. 提前想好測试用例(输入非空等等)进行測试改动代码. 题目链接地址: http://ac.jobdu.com/problem.php? ...

  3. 剑指Offer面试题15(Java版):链表中倒数第K个结点

    题目: 输入一个链表.输出该链表中倒数第k哥结点.  为了符合大多数人的习惯,本题从1開始计数.即链表的尾结点是倒数第1个结点. 比如一个链表有6个结点.从头结点開始它们的值依次是1.2.3,4,5, ...

  4. C++版 - 剑指offer 面试题5:从尾到头打印链表 题解

    面试题5:从尾到头打印链表 提交网址: http://www.nowcoder.com/practice/d0267f7f55b3412ba93bd35cfa8e8035?tpId=13&tq ...

  5. 剑指Offer:面试题12——打印1到最大的n位数(java实现)

    问题描述: 输入数字n,按顺序打印出从1到最大的n位十进制数,比如输入3,则打印出1,2,3一直到最大的3位数即999. 思路1:最简单的想法就是先找出最大的n位数,然后循环打印即可. public ...

  6. 【剑指offer 面试题12】打印1到最大的n位数

    思路: 用n位字符数组表示n位数,通过递归的方式逐层(位)遍历,递归终止时打印. #include "stdio.h" #include "string.h" ...

  7. 剑指Offer面试题:4.从尾到头打印链表

    一.题目:从尾到头打印链表 题目:输入一个链表的头结点,从尾到头反过来打印出每个结点的值. 到解决这个问题肯定要遍历链表.遍历的顺序是从头到尾的顺序,可输出的顺序却是从尾到头.也就是说第一个遍历到的结 ...

  8. 剑指Offer:面试题17——合并两个排序的链表

    题目描述 输入两个单调递增的链表,输出两个链表合成后的链表,当然我们需要合成后的链表满足单调不减规则. 思路1: 分别用p1,p2两个指针扫描两个有序链表,p3指针去构建新链表h3. p1.val & ...

  9. 剑指offer——面试题6:从尾到头打印链表

    #include"iostream" #include"stdio.h" #include"stack" using namespace s ...

随机推荐

  1. webpack

    webpack 通过一个主文件 .js ,webpack把这个文件所有的依赖文件,都处理打包成js文件 webpack 可以干嘛?1.执行打包 (把require()模块化整合成一个js文件给html ...

  2. [UWP]使用AdaptiveTrigger实现自适应布局

    这篇博客将介绍如何在UWP开发中使用AdaptiveTrigger实现自适应布局. 场景1:窗体宽度大于800时,窗体背景色为绿色,窗体在0到800之间为蓝色. XAML Code: <Grid ...

  3. Go语言 模板的使用(一)

    使用Parse package main import ( "html/template" "net/http" ) func SayHello(w http. ...

  4. 使用IntelliJ IDEA 配置Maven(入门)【转】

    1.在IntelliJ IDEA中配置maven 打开-File-Settings  2.新建maven WEB项目 打开-File-New-Project 点击NEXT  点击NEXT  添加的配置 ...

  5. java中注解的使用与实例 (二)

    java 注解,从名字上看是注释,解释.但功能却不仅仅是注释那么简单.注解(Annotation) 为我们在代码中添加信息提供了一种形式化的方法,是我们可以在稍后 某个时刻方便地使用这些数据(通过 解 ...

  6. ECF R9(632E) & FFT

    Description: 上一篇blog. Solution: 同样我们可以用fft来做...就像上次写的那道3-idoit一样,对a做k次卷积就好了. 同样有许多需要注意的地方:我们只是判断可行性, ...

  7. 面向对象课后深入学习(C++ 类的静态成员详细讲解)

    今天在刚申请了博客,一下午都在写那个随笔,所以说好的来看c++的也放在了最后,下星期就考试了,这个类的静态成员是我不是很懂的,在网上 看到一片很详细的博客考下来回去慢慢看. 在C++中,静态成员是属于 ...

  8. KMP匹配算法 - Number Sequence

    Given two sequences of numbers : a[1], a[2], ...... , a[N], and b[1], b[2], ...... , b[M] (1 <= M ...

  9. 利用PHP的ob函数实现生成静态化页面

    之前用过一些开源的CMS管理系统,当时就很好奇后台中的生成HTML静态文件是怎么实现的.今天和同事讨论了下,没想到同事之前做过这类的生成静态页面的功能,果断向他请教了下. 经他讲解后,才知道其实生成静 ...

  10. 怎么取得dropdownlist选中的ID值

    把数据库绑定在dropdownlist中,然后把选中的dropdownlist的项的ID值保存在另外的一个数据库中.怎么取得dropdownlist选中的ID值呢?? this.DropDownLis ...