题目:
给定链表的头指针和一个结点指针,在O(1)时间删除该结点。链表结点的定义如下:
struct ListNode
{
int m_nKey;
ListNode* m_pNext;
};
函数的声明如下:
void DeleteNode(ListNode* pListHead,ListNode* pToBeDeleted);
分析:
在链表中删除一个结点,最常规的做法是从链表的头结点开始,顺序查找要删除的结点,找到之后再删除。由于需要顺序查找,时间复杂度自然就是O(n) 了。
我们之所以需要从头结点开始查找要删除的结点,是因为我们需要得到要删除的结点的前面一个结点。我们试着换一种思路。我们可以从给定的结点得到它的下一个结点。这个时候我们实际删除的是它的下一个结点,由于我们已经得到实际删除的结点的前面一个结点,因此完全是可以实现的。当然,在删除之前,我们需要需要把给定的结点的下一个结点的数据拷贝到给定的结点中。此时,时间复杂度为O(1)。
上面的思路还有一个问题:如果删除的结点位于链表的尾部,没有下一个结点,怎么办?我们仍然从链表的头结点开始,顺便遍历得到给定结点的前序结点,并完成删除操作。这个时候时间复杂度是O(n)。
那题目要求我们需要在O(1)时间完成删除操作,我们的算法是不是不符合要求?实际上,假设链表总共有n个结点,我们的算法在n-1总情况下时间复杂度是O(1),只有当给定的结点处于链表末尾的时候,时间复杂度为O(n)。那么平均时间复杂度[(n-1)*O(1)+O(n)]/n,仍然为O(1)。
核心代码:
/**
* 给定链表的头指针和一个结点指针,在O(1)时间删除该结点
* @param pHead 链表头结点
* @param pToBeDeleted 要删除的链表结点
* @return
*/
public ListNode deleteNode(ListNode pHead, ListNode pToBeDeleted) {
ListNode pNode = pHead;
if (pHead == null || pToBeDeleted == null)
return null;
if (pToBeDeleted.m_pNext != null) {//如果pToBeDeleted不是链表的尾结点
//将pToBeDeleted结点的下一个结点覆盖pToBeDeleted结点
ListNode pNext = pToBeDeleted.m_pNext;
pToBeDeleted.m_nKey = pNext.m_nKey;
pToBeDeleted.m_pNext = pNext.m_pNext;
pNext = null;
} else {//如果pToBeDeleted是链表的尾结点
//遍历链表找到pToBeDeleted的上一个结点
while(pNode.m_pNext!=pToBeDeleted)
pNode = pNode.m_pNext;
pNode.m_pNext = null;
pToBeDeleted = null;
}
return pHead;
}