其次十八题 在O(1)时间内删除指定节点
第二十八题 在O(1)时间内删除指定节点
题目:给定链表的头指针和一个结点指针,在O(1)时间删除该结点。
在链表中删除一个结点,最常规的做法是从链表的头结点开始,顺序查找要删除的结点,找到之后再删除。由于需要顺序查找,时间复杂度自然就是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) #include <iostream> using namespace std; struct Node { int m_value; Node *m_next; }; //生成链表 void makeNode(Node *&ptr,int key) { if (ptr==nullptr) { Node *m_currentptr=new Node(); if (m_currentptr!=nullptr) { m_currentptr->m_value=key; m_currentptr->m_next=nullptr; ptr=m_currentptr; } } else { makeNode(ptr->m_next,key); } } //删除节点 void DeleteNode(Node* pListHead, Node* pToBeDeleted) { if (pListHead==nullptr||pToBeDeleted==nullptr) { return; } //当删除的节点不为尾节点的时候 if (pToBeDeleted!=nullptr) { Node *thnode=nullptr; thnode=pToBeDeleted->m_next; pToBeDeleted->m_value=thnode->m_value; pToBeDeleted->m_next=thnode->m_next; delete thnode; thnode=nullptr; } //当删除的节点为尾节点的时候 else { Node* pNode = nullptr; pNode=pListHead; if (pNode->m_next!=pToBeDeleted) { pNode=pNode->m_next; } pNode->m_next=nullptr; delete pToBeDeleted; pToBeDeleted=nullptr; } while (pListHead!=nullptr) { cout<<pListHead->m_value<<endl; pListHead=pListHead->m_next; } } int main() { Node *ptr=nullptr; makeNode(ptr,1); makeNode(ptr,2); makeNode(ptr,3); makeNode(ptr,4); makeNode(ptr,5); makeNode(ptr,6); makeNode(ptr,7); //链表的头节点 Node *m_head=nullptr; m_head=ptr; //链表中需要删除的节点 Node *m_deletenode=nullptr; m_deletenode=m_head->m_next->m_next; DeleteNode(m_head,m_deletenode); delete ptr; ptr=nullptr; return 0; }