题目:试编写算法将带头节点的单链表就地逆置,所谓“就地”就是指空间复杂度为O(1),只需要常数个存储空间。
第一种方法比较简单,就是采用头插法,因为头插法每次到来的新结点都会插入到头结点的下第一个位置。
void Reverse_List(LinkList &L){
if(L->next==NULL){
return; //如果L指向的是空链表,退出函数
}
LNode *p,*s;
p = L->next;
L->next = NULL;
while(!p){
s = p; //s指向当前链表的第一个结点
p = p->next; //p指向当前链表的下一个结点
s->next = L->next; //头插操作
L->next = s;
}
}
第二种思路是用三个指针指向相邻的三个结点,将中间结点的指针域指向它的前驱结点,然后所有指针进行后移,遍历完链表后将头结点指针域连接到尾节点就实现了逆置。不过这种方法需要单链表结点在三个或以上,对于一般题目需要考虑很多特殊情况。
代码如下:
/*需要创建三个指针,分别是*pre*/
void Reverse_List(LinkList &L){
if(L->next==NULL){
return; //如果L指向的是空链表,退出函数
}
LNode *pre = L->next;//pre指向头结点的下一个结点
if(!pre->next){ //如果链表只有一个结点,就不用了逆置了
return;
}
LNode *p = pre->next;
if(!p->next){ //如果链表只有两个结点
pre->next = NULL;
p->next = pre;
L->next = p; //逆置操作完成
return;
}
LNode *rear = p->next;//当链表有三个或三个以上结点时
pre->next = NULL;//完成逆置后,原本的第一个结点会变成尾结点,需要提前指向NULL
//不存在断链的风险,因为相邻结点都已经有指针标记了。
while(!rear){
p->next = pre;
pre = p;
p = rear;
rear = rear->next;
} //退出循环时,rear=NULL,而p指向整个链表的第一个有效节点上,也就是逆置前的尾节点
L->next = p; //连接头结点和链表。
}
另外补充一下:
如果函数需要返回链表,应该这么定义
LinkList Reverse_List(LinkList L){
...
return L;
}
如果没要求函数返回单链表,那么函数的形参调用需要是引用链表的形式,这样函数执行完,原本的链表也会被修改。
void Reverse_List(LinkList &L);