方法一:非递归法
1.单链表反转,可以不使用头结点。
2.初始化阶段:p=L(当前正序链表的第一个结点);
pHead=NULL,返回逆序反转链表的头结点;
pPrev=NULL,保存p的前一个结点,初始化为NULL,之后随着往后反转,保存已反转部分的第一个结点。
3.从正序链表的第一个结点开始,若当前结点p不为空,①保存正序表中p所指向的下一个元素pNext=p->next。②判断pNext是否为空,若为空,说明遍历到最后一个元素,返回头结点;若不为空,执行三次更新:p->next =pPrev;(当前结点的下一个结点,是上一步保留的已反转子表的第一个元素);pPrev=p;(再新加入反转元素之后,更新pPrev结点);p=p->next;(更新要搜索的结点)。
具体实现如下:
/*反转链表*/
Linklist ListReverse(Linklist L)
{
Linklist p,pNext;
Linklist pPrev=NULL;
Linklist pHead=NULL;
p=L;
while(p!=NULL)
{
pNext=p->next;
if (pNext==NULL)
pHead=p;
p->next=pPrev;
pPrev =p;
p=pNext;
}
return pHead;
}
int main()
{
Linklist L;
InitList1(&L); //初始化函数、插入数据函数、统计长度函数可参考前面几篇中的例子
int len1=ListLength1(L);
printf("%d\n",len1);
for (int i=1;i<=5;i++)
{
ListInsert1(&L,1,i);
}
int len2=ListLength1(L);
printf("%d\n",len2);
Linklist p =L;
for(int i=1;i<=len2;i++)
{
printf("%d\n",p->next->data);
p=p->next;
}
ListTraverse1(L);
Linklist LReverse=ListReverse(L);
int lenLReverse=ListLength1(LReverse);
//反转之后,无头结点,直接从第一个元素输出。
for(int i=1;i<=lenLReverse;i++)
{
printf("\n%d",LReverse->data);
LReverse=LReverse->next;
}
getchar();
}
扩展:
若不对链表做反转,直接做逆序输出,则使用递归法即可。首先找到最后一个元素,然后依次输出。
/*正序链表进行逆序输出*/
Status ListPrintReverse(Linklist L)
{
if (L->next!=NULL)
{
ListPrintReverse(L->next);//递归调用本函数
printf("\n%d",L->next->data);
}
return OK;
}
方法二:递归法:注意递归之后,返回逆序后的第一个结点指针;注意逆序后最后一个结点指向空。否则可能会造成循环。
步骤:判断是否为空或只有一个结点。①若是,则直接返回该头结点。②若不是,则进入递归步骤,找到最后一个结点,并记录新的头结点。然后对最后两个结点进行反转。最后一个结点指向倒数第二个结点;倒数第二个结点指向空。依据此规律处理前面的结点即可。
/*反转链表:2.递归法*/
/*递归法要考虑:第一个结点位置、最后一个结点指向NULL*/
Linklist ListReverseRecursion(Linklist L,Linklist &head)
{
Linklist p=L;
Linklist pPrev=NULL;
if(p==NULL || p->next ==NULL)
{
head =p;
return p;
}
else
{
Linklist temp=ListReverseRecursion(p->next,head);
p->next->next=p;
p->next=NULL;
return temp;
}
}