单链表反转是链表的一种基本操作。网上看了一些文章,介绍的还是都很详细。自己也尝试着写了一些代码,调试通过。链表的反转方法有很多种。采用游标指针遍历,修改next指针是常用的方法。一般地,需要定义三个指针,比如本文中,定义为
Node *cur,*pNext,*pre;
其中,cur用来保存反转后的头指针;pNext是游标,用来遍历链表并将各个节点的next指针值修改为前一个节点地址;而pre指针用来在pNext修改next指针前,将next指向的下一个节点指针(即尚未反转的子链的头指针)临时保存。
既然pNext为遍历游标,故而循环条件即为判断pNext非空。
需要注意的是,第一个数据节点反转结束后成为尾节点,因而其next指针需要置空。
我写的代码的两种思路:一是如前面所述,定义三个指针;另外一种思路是不单独定义临时指针pre,而是用第一个数据节点(即反转后的尾节点)的next指针作为临时指针。同时,cur指针也可以不用,直接用h-next保存转换后的第一个数据指针即可。
上代码:
头文件LinkedNode.h
#ifndef LINKEDNODE_H_INCLUDED
#define LINKEDNODE_H_INCLUDED
#endif // LINKEDNODE_H_INCLUDED
#include <stdio.h>
#include <stdlib.h>
typedef int DATA; //定义通用数据类型
typedef struct _node
{
DATA data;
struct _node *next;
}Node,*LinkedNode;
LinkedNode Create();//创建空链表,返回头指针
LinkedNode CreateN(int *N);//构造一个能读入N个初始数据节点的链表,当输入的数据不足N个或有非法数据时,只取前面的有效数据,并修改N值
void Print(LinkedNode h);//按节点先后顺序呢输出
int Reverse_1(LinkedNode h);//反转链表
int Reverse_2(LinkedNode h);
int Reverse_3(LinkedNode h);
int Reverse_4(LinkedNode h);
四个反转方法Reverse_1和Reverse_2是思路一,区别是Reverse_2省略了cur变量。Reverse_3和Reverse_4是思路二,区别是Reverse_4初始化的变量不同,从而循环变量和循环顺序不一样。这样主要是为了练习,说明写代码时变量的定义方式和循环方式都是可以改变的。有些时候可以优化代码。
算法文件LinkedNode.c
#include "LinkedNode.h"
//创建空链表,返回头指针
LinkedNode Create()
{
LinkedNode h;
h=malloc(sizeof(Node));
if(h==NULL) return NULL;
else
{
h->next=NULL;
}
return h;
}
构造一个能读入N个初始数据节点的链表,当输入的数据不足N个时,数据置NULL
LinkedNode CreateN(int *N)
{
LinkedNode h=Create();
if(h==NULL)return NULL;
int i=1;
DATA d;
Node *p=h;
printf("\nfunc:CreateN:请输入数据个数:\n");
if(scanf("%d",N)==1)
{
if(*N<1)
{
printf("\nfunc:CreateN:您输入的数字必须是大于0的整数,程序将结束!\n");
return;
}
printf("\nfunc:CreateN:您打算输入的数据个数为%d,请依次输入数据,并按回车键结束\n",*N);
while(i<=*N&&scanf("%d",&d)==1)
{
Node *pTemp=malloc(sizeof(Node));
if(pTemp!=NULL)
{
pTemp->data=d;
p->next=pTemp;
p=p->next;
i++;
}
p->next=NULL;
}
*N=--i;
printf("\nfunc:CreateN:您已经输入%d个有效数据,以下是您刚才输入的有效数据:\n",*N);
Print(h);
}
return h;
}
//按节点先后顺序输出
void Print(LinkedNode h)
{
if(h==NULL||h->next==NULL)
{
printf("\nfunc:Print:链表不存在或者为空!\n");
return;
}
Node *p=h->next;
printf("\n");
while(p)
{
printf("%d\t",p->data);
p=p->next;
}
printf("\n");
}
//反转链表,带表头指针,用三个指针
//cur:记录头指针;pNext:游标,操作指针;pre:临时指针,用来临时记录未转换的链表的头位置
//返回值:循环次数
//循环前对游标变量pNext初始化,如果将pre也初始化,则可以更改训话顺序
//注意翻转后的尾指针(即原始的第一个数据指针)的next需要置空
int Reverse_1(LinkedNode h)
{
int i=0;//记录循环次数
if(h==NULL||h->next==NULL)
{
printf("\nfunc:Reverse:链表不存在或者为空!\n");
return;
}
Node *cur,*pNext,*pre;
cur=h->next;
pNext=cur->next;
cur->next=NULL;
while(pNext)
{
pre=pNext->next;
pNext->next=cur;
cur=pNext;
pNext=pre;
i++;
}
h->next=cur;
return i;
}
//与Reverse_1本质一致,但不定义cur指针,而是直接修改h->next,从而减少栈中变量数量
int Reverse_2(LinkedNode h)
{
int i=0;
if(h==NULL||h->next==NULL)
{
printf("\nfunc:Reverse_2:链表不存在或者为空!\n");
return;
}
Node *pNext,*pre;//pNext为游标指针,用来操作next指针反转,pre指针为临时指针,用来临时记录未操作部分的表头
pNext=h->next->next;//定义游标初始位置,即转换后的尾指针位置
h->next->next=NULL;//尾指针不使用,置空
while(pNext)
{
pre=pNext->next;
pNext->next=h->next;
h->next=pNext;
pNext=pre;
i++;
}
return i;
}
//反转链表,带表头,这种方法是将后面的节点依次插入h的后面
//为了说明使用h->next->next作为存储临时指针,这里定义Node *tail=h->next
//这样好处是不用另外定义一个临时指针pre,且循环结束时,tail自动只想NULL,不用专门置空操作
int Reverse_3(LinkedNode h)
{
int i=0;
if(h==NULL||h->next==NULL)
{
printf("\nfunc:Reverse_2:链表不存在或者为空!\n");
return;
}
Node *tail,*pNext;
tail=h->next;
pNext=tail->next;//游标指针初始位置,如果此处不初始化,应该把pNext=tail->next放在循环开始,且循环条件为tail->next!=NULL
while(pNext!=NULL)
{
tail->next=pNext->next;
pNext->next=h->next;
h->next=pNext;
pNext=tail->next;
i++;
}
return i;
}
//方法同Reverse_3,但pNext在循环前不初始化,故而循环条件和顺利都改变
int Reverse_4(LinkedNode h)
{
int i=0;
if(h==NULL||h->next==NULL)
{
printf("\nfunc:Reverse_2:链表不存在或者为空!\n");
return;
}
Node *tail=h->next;
Node *pNext;
while(tail->next!=NULL)
{
pNext=tail->next;
tail->next=pNext->next;
pNext->next=h->next;
h->next=pNext;
i++;
}
return i;
}
下面是main.c 文件,包含一个测试函数
#include "LinkedNode.h"
#include "tree.h"
void testReverse();
int main()
{
testReverse();
return 0;
}
void testReverse()
{
int N=0;
int nCount;
LinkedNode h=CreateN(&N);
if(N<1)
{
printf("\nfunc:testReverse:您输入的数字必须是大于0的整数,程序将结束!\n");
//return;
}
nCount=Reverse_4(h);
if(nCount<1)
{
printf("\nfunc:testReverse:共进行了0次链表反转循环,程序将结束\n");
//return;
}
printf("\nfunc:testReverse:在本次链表反转时,共进行了%d次循环\n",nCount);
printf("\nfunc:testReverse:以下是反转后的结果\n");
printf("\n=========================================================\n");
Print(h);
}
几个函数测试都通过。编译环境是GCC。下面是两组输入情形: