删除链表的节点
1、题目
给定单向链表的头指针和一个节点指针,定义一个函数在O(1)时间内删除该节点。
输入参数:单向链表的头指针pHead,节点指针pToBeDeleted
输出结果:无
2、解题
这道题的关键在于如何在时间复杂度为O(1)的情况下删除指定节点。
因此,我们可以想到覆盖的方法:先获得要删除节点的下一节点,将下一节点的值赋值给删除节点,再将删除节点的指针指向下下个节点,最后删除下一节点即可,通过后面覆盖前面的方式进行删除。
这道题的难度在于对删除节点不同情况的划分。
需要划分为三种情况:
- 删除的节点非尾结点(隐含了存在两个或以上节点的前提),覆盖删除
- 删除的节点为尾结点
- 若链表中只有一个节点,即头节点等于尾结点,直接删除即可
- 若链表中有多个节点,需要找到尾结点的前一个节点,再进行删除操作
3、代码
void DeleteNode(ListNode** pHead, ListNode* pToBeDeleted) {
//鲁棒性检查
if (!pHead || !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 (pToBeDeleted == *pHead) {
//当只有一个节点时,即尾结点和头节点重合时
delete pToBeDeleted;
pToBeDeleted = nullptr;
*pHead = nullptr;
}
else {
//当链表中有多个节点时,需要先找到尾结点的前一个节点
ListNode* pNode = *pHead;
while (pNode->m_pNext != pToBeDeleted) {
pNode = pNode->m_pNext;
}
pNode->m_pNext = nullptr;
delete pToBeDeleted;
pToBeDeleted = nullptr;
}
}
4、注意点
- 之所以删除尾结点时,要找到其前一个节点而不是直接删除节点,是为了防止出现空指针异常
- 注意鲁棒性的检查
- 链表中只有一个节点时,在删除节点后要将头指针置空
5、代码的可行性
上述代码是基于一个假设:要删除的节点的确在链表中。我们需要使用O(n)的时间才能判断节点是否在链表中,但又受到O(1)时间的限制,因此我们不得不把确保节点在链表中的责任交给函数DeleteNode的调用者。在面试的时候,我们可以和面试官探讨这个假设,把问题考虑得更加全面。