您的位置:首页 > 职场人生

给定链表的头指针和一个结点指针,在O(1) 时间删除该结点

2008-09-07 10:21 369 查看
题目:给定链表的头指针和一个结点指针,在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
)给定的要删除的结点不是链表的头结点。不考虑第一个假设对代码的鲁棒性是有影响的。至于第二个假设,当整个列表只有一个结点时,代码会有问题。但这个假
设不算很过分,因为在有些链表的实现中,会创建一个虚拟的链表头,并不是一个实际的链表结点。这样要删除的结点就不可能是链表的头结点了。当然,在面试
中,我们可以把这些假设和面试官交流。这样,面试官还
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐