记录来自《剑指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。