记录来自《剑指offer》上的算法题。
题目如下:
给定单向链表的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点。
结点定义如下:
struct ListNode{
int m_nValue;
ListNode* m_pNext;
};
最常规的删除链表结点方法是从头结点开始遍历,然后找到要删除结点的前一个结点,让它指向要删除结点的下一个结点,但是这种做法的时间是O(n),而现在要求时间是O(1),所以就必须换一种方法,解法如下:
// 在O(1)时间内删除给定结点
void DeleteNode(ListNode** pListHead, ListNode* pToBeDeleted){
if (!pListHead || !pToBeDeleted)
return;
if (pToBeDeleted->m_pNext != NULL){
// 要删除的结点不是尾结点
ListNode* pNext = pToBeDeleted->m_pNext;
pToBeDeleted->m_nValue = pNext->m_nValue;
pToBeDeleted->m_pNext = pNext->m_pNext;
delete pNext;
pNext = NULL;
}
else if (*pListHead == pToBeDeleted){
// 链表只有一个结点,删除头结点,也就是尾结点
delete pToBeDeleted;
pToBeDeleted = NULL;
*pListHead = NULL;
}
else{
// 链表有多个结点,删除尾结点,采取从头开始遍历
ListNode* pNode = *pListHead;
while (pNode->m_pNext != pToBeDeleted){
pNode = pNode->m_pNext;
}
pNode->m_pNext = NULL;
delete pToBeDeleted;
pToBeDeleted = NULL;
}
}
新解法的思路是将待删除结点i的下一个结点j直接覆盖在要删除的结点上,然后再将结点j删除,这样就不需要找到结点i的前一个结点了。当然,这是一般情况,如果待删除结点是一个尾结点,是有多个结点的链表的尾结点,那么就只能采用最常规的办法,从头开始遍历,但是前面n-1个非尾结点的时间复杂度是O(1),所以总的平均时间复杂度是[(n-1)*O(1) + O(n)]/n,结果还是O(1)。
测试代码如下:
// 在链表结尾插入一个结点
void AddToTail(ListNode** pHead, int value){
ListNode* pNew = new ListNode();
pNew->m_nValue = value;
pNew->m_pNext = NULL;
if (*pHead == NULL){
*pHead = pNew;
}
else
{
ListNode* pNode = *pHead;
while (pNode->m_pNext != NULL)
pNode = pNode->m_pNext;
pNode->m_pNext = pNew;
}
}
// 输出链表
void printList(ListNode* pHead){
ListNode* p = pHead;
if (!p)
cout << "List is empty!\n";
while (p != NULL){
cout << p->m_nValue;
if (p->m_pNext == NULL)
cout << "\n";
else{
cout << ", ";
}
p = p->m_pNext;
}
}
// 测试
int main(void){
ListNode* t = NULL;
for (int i = 0; i < 10; i++)
AddToTail(&t, i);
cout << "List1:\n";
printList(t);
// 删除多个结点链表中的一个结点
ListNode* pNode1 = t->m_pNext;
DeleteNode(&t, pNode1);
printList(t);
// 删除多个结点链表中的头结点
ListNode* pNode2 = t;
DeleteNode(&t, pNode2);
printList(t);
// 删除多个结点链表中的尾结点
ListNode* pNode3 = t;
while (pNode3->m_pNext != NULL)
pNode3 = pNode3->m_pNext;
DeleteNode(&t, pNode3);
printList(t);
// 从只有一个结点的链表中删除唯一的结点
ListNode* t2 = NULL;
AddToTail(&t2, 2);
cout << "List2:";
printList(t2);
DeleteNode(&t2, t2);
printList(t2);
// 指向链表头结点指针的是NULL指针
DeleteNode(&t2, t2);
// 指向要删除结点的是NULL指针
pNode3 = NULL;
DeleteNode(&t, pNode3);
system("pause");
return 0;
}
更完整的例子可以查看我的Github。