问题:给定一个单向的头指针和一个结点指针,定义一个函数在O(1)时间删除该结点。
具体实现代码如下;
因为单链表的最大弊病就是查找是单向的,只能向前不能后退!精神可嘉,但是带来了限制。比如一般我我们考虑删除链表的一个给定结点都是要从头结点遍历找到要删除的结点的前驱结点,然后才能正确的删除给定的结点,其查找的时间复杂度为O(N),能不能不进行查找使删除的效率为O(1)呢?答案是肯定的。下面来看一看!
如上图所示,如果我们要删除结点4,一般我们需要找到其前驱结点3,然后才能删除4,这样的时间复杂度是O(N);
但是也有一种方法就是不需要遍历找其前驱结点,具体方法是:因为要删除4结点,那么4这个节点的数据已经没有意义,所以,我们可以采用覆盖的思想,因为4结点的后继结点我么是可以轻易的拿到的,所以就采用覆盖的思想,将5结点赋值到4结点的空间,然后再删除结点5,这样就不用遍历了!
但是里面的一个细节的注意,如果删除的结点是尾结点那么就不能采用此种方法,需要特殊处理,终究还是逃不过遍历这条路。具体实现代码如下;
#include <iostream>
using namespace std;
struct List
{
int number;
List *pNext;
}list;
List *pHead=NULL;
List *pEnd=NULL;
List *pNode=NULL;
List *todelete=NULL;
void createlist()
{
int data;
cout<<"please input the data:"<<endl;
cin>>data;
while(0!=data)
{
pNode=new List;
pNode->number=data;
pNode->pNext=NULL;
if(3==data)
{
todelete=pNode;
}
if(NULL==pHead)
{
pHead=pNode;
pEnd=pNode;
}
else
{
pEnd->pNext=pNode;
pEnd=pNode;
}
cin>>data;
}
}
void Delete1(List *pHead,List *todelete)
{
if(!pHead || !todelete)
return ;
//要删除的不是尾结点;时间复杂度为O(1);
if(NULL != todelete->pNext)
{
List *temp=todelete->pNext;
todelete->number=temp->number;
todelete->pNext=temp->pNext;
delete temp;
temp=NULL;
}
//删除的是尾结点;需要遍历链表,时间复杂度为O(N);
if(NULL==todelete->pNext)
{
List *temp=pHead;
while(temp->pNext!=todelete)
{
temp=temp->pNext;
}
temp->pNext=NULL;
delete todelete;
todelete=NULL;
}
//删除的是头结点也是尾结点;
if(pHead==todelete)
{
delete pHead;
pHead=NULL;
todelete=NULL;
}
}
void showlist()
{
List *temp=pHead;
cout<<"List:";
while(temp)
{
cout<<temp->number<<" ";
temp=temp->pNext;
}
cout<<endl;
}
int main()
{
createlist();
showlist();
List *pTemp=pHead;
while(pTemp!=todelete && pTemp)
{
pTemp=pTemp->pNext;
}
/*注意次todelete一定在链表中,因为我是在创建链表时取得,如果不确定的话我们还得对该节点
进行判断看是否在改链表中,所以要进行上述的判断;*/
if(pTemp)
{
Delete1(pHead,todelete);
}
showlist();
system("pause");
return 0;
}
运行结果:
从上可以看出,对于n-1个非尾结点而言,我们可以在O(1)时间内删除,但是对于尾结点而言我们仍需要遍历链表,时间复杂度为O(N),因此平均时间复杂度为:
[(n-1)*O(1)+O(N)]/N,结果还是O(1),效率有所提高。