作者:何海涛

出处:http://zhedahht.blog.163.com/

题目:给定链表的头指针和一个结点指针,在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)。

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

参考代码:

///////////////////////////////////////////////////////////////////////

// Delete a node in a list

// Input: pListHead - the head of list

//        pToBeDeleted - the node to be deleted

///////////////////////////////////////////////////////////////////////

void DeleteNode(ListNode* pListHead, ListNode* pToBeDeleted)

{

      if(!pListHead || !pToBeDeleted)

            return;

      // if pToBeDeleted is not the last node in the list

      if(pToBeDeleted->m_pNext != NULL)

      {

            // copy data from the node next to pToBeDeleted

            ListNode* pNext = pToBeDeleted->m_pNext;

            pToBeDeleted->m_nKey = pNext->m_nKey;

            pToBeDeleted->m_pNext = pNext->m_pNext;

            // delete the node next to the pToBeDeleted

            delete pNext;

            pNext = NULL;

      }

      // if pToBeDeleted is the last node in the list

      else

      {

            // get the node prior to pToBeDeleted

            ListNode* pNode = pListHead;

            while(pNode->m_pNext != pToBeDeleted)

            {

                  pNode = pNode->m_pNext;            

            }

            // deleted pToBeDeleted

            pNode->m_pNext = NULL;

            delete pToBeDeleted;

            pToBeDeleted = NULL;

      }

} 

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

作者:何海涛

出处:http://zhedahht.blog.163.com/

程序员面试题精选100题(33)-在O(1)时间删除链表结点[数据结构]的更多相关文章

  1. 程序员面试题精选100题(16)-O(logn)求Fibonacci数列[算法]

    作者:何海涛 出处:http://zhedahht.blog.163.com/ 题目:定义Fibonacci数列如下: /  0                      n=0 f(n)=      ...

  2. 程序员面试题精选100题(38)-输出1到最大的N位数[算法]

    作者:何海涛 出处:http://zhedahht.blog.163.com/ 题目:输入数字n,按顺序输出从1最大的n位10进制数.比如输入3,则输出1.2.3一直到最大的3位数即999. 分析:这 ...

  3. 《剑指offer》第十八题(在O(1)时间删除链表结点)

    // 面试题18(一):在O(1)时间删除链表结点 // 题目:给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该 // 结点. #include <iostream> ...

  4. 33.在O(1)时间删除链表结点[DeleteListNode]

    [题目] 给定链表的头指针和一个结点指针,在O(1)时间删除该结点.链表结点的定义如下:  C++ Code  123456   struct ListNode {     int        m_ ...

  5. 【面试题013】在O(1)时间删除链表结点

    [面试题013]在O(1)时间删除链表结点  我们要删除结点i,我们可以把结点i的下一个结点j的内容复制到结点i,然后呢把结点i的指针指向结点j的下一个结点.然后在删除结点j. 1.如果结点i位于链表 ...

  6. 面试题18(一):在O(1)时间删除链表结点

    // 面试题18(一):在O(1)时间删除链表结点 // 题目:给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该 // 结点.链表结点与函数的定义如下: // struct Lis ...

  7. [经典面试题]在O(1)时间删除链表结点

    [题目] 给定链表的头指针和一个结点指针.在O(1)时间删除该结点.链表结点的定义例如以下: struct ListNode { int        value; struct ListNode*  ...

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

    一.题目:在O(1)时间删除链表结点 题目:给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点. 原文采用的是C/C++,这里采用C#,节点定义如下: public class ...

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

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

随机推荐

  1. iOS Instruments内存检测使用

    Instruments 可以帮我们了解到应用程序使用内存的几个方面: 全局内存使用情况(Overall Memory Use): 从全局的角度监测应用程序的内存使用情况,捕捉非预期的或大幅度的内存增长 ...

  2. Struts—自定义一个简单的mystruct

    传统mvc开发总结: 1. 跳转代码写死,不灵活 2. 每次都去写servlet,web.xml中配置servlet! (配置目的: 请求, Servlet处理类) 一个简单的struct案例,描述如 ...

  3. 对PostgreSQL cmin和cmax的理解

    看例子: 开两个终端来对比: 在终端A: [pgsql@localhost bin]$ ./psql psql () Type "help" for help. pgsql=# b ...

  4. 用CSS让网页背景图片居中的方法

    网页背景居中的方法有很多种的.这里介绍一些用CSS让背景图片居中的方法. 让背景图片居中的第一个方法是用像素设定,很多都用这种,但是也是最麻烦的: <div style="width: ...

  5. .NET连接MySql数据库的方法及示例

    方法一: 使用MySQL推出的MySQL Connector/Net组件, 该组件是MySQL为ADO.NET访问MySQL数据库设计的.NET专用访问组件.完成该组件后,需要在项目中引用这个组件,也 ...

  6. PAT 1017

    1017. Queueing at Bank (25) Suppose a bank has K windows open for service. There is a yellow line in ...

  7. com.service.impl

    package com.service.impl; import java.util.ArrayList; import java.util.LinkedHashMap; import java.ut ...

  8. js中的preventDefault和stopPropagation

    首先讲解一下js中preventDefault和stopPropagation两个方法的区别:      preventDefault方法的起什么作用呢?我们知道比如<a href=" ...

  9. SQL 四种连接:内连接、左外连接、右外连接、全连接--转载

    原文:http://zwdsmileface.iteye.com/blog/2191730 个人理解 内连接(INNER JOIN)(典型的连接运算,使用像   =   或   <>   ...

  10. Using zend-navigation in your Album Module

    Using zend-navigation in your Album Module In this tutorial we will use the zend-navigation componen ...