剑指offer--面试题13
题目:以O(1)的时间复杂度删除单链表中的某个节点
自己所写代码如下:
//以O(1)时间删除链表节点 //要求:单向链表,头指针,待删节点指针 //链表节点 struct ListNode { int m_nValue; ListNode* m_pNext; }; //O(n)的解法:从头遍历,找到pToBeDeleted所指节点的前一个节点再进行删除 void DeleteNode(ListNode** pListHead, ListNode* pToBeDeleted) { if(pListHead == NULL || *pListHead == NULL || pToBeDeleted == NULL) return; ListNode* pNode = *pListHead; if(pNode == pToBeDeleted) //对pToBeDeleted指向头结点情况的处理 { *pListHead = (*pListHead)->m_pNext; delete pToBeDeleted; pToBeDeleted = NULL; } else { while(pNode->m_pNext != NULL && pNode->m_pNext != pToBeDeleted) pNode = pNode->m_pNext ; if(pNode->m_pNext == NULL) { cout<<"pToBeDeleted不在链表中!"<<endl; return; } pNode->m_pNext = pToBeDeleted->m_pNext; delete pToBeDeleted; pToBeDeleted = NULL; } } //O(1)的解法:复制后一个节点以覆盖待删节点,再删除重复的后一个节点 void DeleteNode(ListNode** pListHead, ListNode* pToBeDeleted) { if(pListHead == NULL || *pListHead == NULL || pToBeDeleted == NULL) return; ListNode* pNode = *pListHead; if(pNode == pToBeDeleted) { *pListHead = (*pListHead)->m_pNext; delete pToBeDeleted; pToBeDeleted = NULL; } else { pNode = pToBeDeleted->m_pNext; //排除pToBeDeleted指向尾节点的情形 if(pNode == NULL) { pNode = *pListHead; while(pNode->m_pNext != pToBeDeleted) pNode = pNode->m_pNext ; pNode->m_pNext = NULL; delete pToBeDeleted; pToBeDeleted = NULL; } else { pToBeDeleted->m_nValue = pNode->m_nValue; pToBeDeleted->m_pNext = pNode->m_pNext; delete pNode; pNode = NULL; } } }
在以上O(1)的代码中,自己的想法有些呆板,具体来说:采用复制覆盖的方法则应考虑的是pToBeDeleted指向尾节点的特殊情况(此时,无法复制!)
而非pToBeDeleted指向头结点的情况(这是O(n)的特殊情况!)!!!
O(n)的方法:需要找前驱节点,所以考虑头结点的特殊情况;
O(1)的方法:需要找后继节点,所以考虑尾节点的特殊情况。
代码修改如下:
//O(1)的解法:复制后一个节点以覆盖待删节点,再删除重复的后一个节点 void DeleteNode(ListNode** pListHead, ListNode* pToBeDeleted) { if(pListHead == NULL || *pListHead == NULL || pToBeDeleted == NULL) return; ListNode* pNode = pToBeDeleted->m_pNext; //排除pToBeDeleted指向尾节点的情形 if(pNode == NULL) { pNode = *pListHead; if(pNode == pToBeDeleted) { delete pToBeDeleted; *pListHead = pToBeDeleted = NULL; } else { while(pNode->m_pNext != pToBeDeleted) pNode = pNode->m_pNext; pNode->m_pNext = NULL; delete pToBeDeleted; pToBeDeleted = NULL; } } else { pToBeDeleted->m_nValue = pNode->m_nValue; pToBeDeleted->m_pNext = pNode->m_pNext; delete pNode; pNode = NULL; } }
和参考代码相一致!赞一个!
总结:1、突破常规思维,删除节点不一定需要从头遍历链表,可以用下一结点复制并覆盖待删节点,最后再删除重复的下一结点。
2、考虑问题全面性:若待删节点为尾节点,则下一个节点为空;若整个链表仅一个节点,删除后,头结点同时设为NULL。
这些都需要特殊对待!!!
清醒时做事,糊涂时读书,大怒时睡觉,独处时思考; 做一个幸福的人,读书,旅行,努力工作,关心身体和心情,成为最好的自己
-- 共勉