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

《剑指offer》(面试题13):在O(1)时间删除链表结点

2018-02-01 18:35 483 查看
题目:给定单向链表的头指针和一个结点指针,定义一个函数在 O(1)时间删除该结点。

之所以需要从头开始查找,是因为我们需要得到将删除的结点的前面一个结点。在单向链表中,结点中没有指向前一个结点的指针,所以只好从链表的头结点开始顺序查找。

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

上述思路还有一个问题:如果要删除的结点位于链表的尾部,那么它就没有下一个结点,怎么办?我们仍然从链表的头结点开始,顺序遍历得到该结点的前序结点,并完成删除操作。

最后需要注意的是,如果链表中只有一个结点,而我们又要删除链表的头结点(也是尾结点),此时我们在删除结点之后,还需要把链表的头结点设置为 NULL。

值得注意的是,上述代码仍然不是完美的代码,因为它基于一个假设:要删除的结点的确在链表中。我们需要在 O(n) 的时间才能判断链表中是否包含某一结点。但是受到 O(1) 时间的限制。在面试的时候,我们可以和面试官推论这个假设,这样面试官就会觉得我们考虑问题非常全面。


解题思路



法一:顺序遍历(时间复杂度O(n))

从单向链表中删除一个结点,最常规的做法就是从链表的头结点开始,顺序遍历查找要删除的结点,并在链表中删除该结点。之所以需要从头开始查找,是因为我们需要得到将被删除的节点的前面一个结点。在单向链表中,结点中没有指向前一个结点的指针,所以只好从链表的头结点开始顺序查找。

方法二:复制结点(时间复杂度O(1))

当然,我们并不是非得得到前一个结点才能来删除该结点。上图C中,我们要删除结点i,先把i的下一个结点j的内容复制到i,然后把i的指针指向结点j的下一个结点。此时再删除节点j,其效果刚好是把结点i给删除了。

但是,这种思路,需要考虑删除尾结点的问题,这个时候只能顺序遍历。同时,还要注意如果链表中只有一个结点的情况,记得删除之后把头结点设置为NULL。

c 语言实现:
void DeleteNode(ListNode** pListHead, ListNode* pToBeDeleted)
{
if(!pListHead || !pToBeDeleted)
return;

// 要删除的结点不是尾结点
if(pToBeDeleted->m_pNext != nullptr)
{
ListNode* pNext = pToBeDeleted->m_pNext;
pToBeDeleted->m_nValue = pNext->m_nValue;
pToBeDeleted->m_pNext = pNext->m_pNext;

delete pNext;
pNext = nullptr;
}
// 链表只有一个结点,删除头结点(也是尾结点)
else if(*pListHead == pToBeDeleted)
{
delete pToBeDeleted;
pToBeDeleted = nullptr;
*pListHead = nullptr;
}
// 链表中有多个结点,删除尾结点
else
{
ListNode* pNode = *pListHead;
while(pNode->m_pNext != pToBeDeleted)
{
pNode = pNode->m_pNext;
}

pNode->m_pNext = nullptr;
delete pToBeDeleted;
pToBeDeleted = nullptr;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: