链表基础和链表的插入删除可以看上两篇博客!
链表基础1(超简单)–创建一个长度为n的单链表
链表基础2(超简单)–单链表的插入和删除
这一节我们来看一下链表的逆序,稍微有一丢丢的难度!
链表是由一个个的节点构成的嘛!话不多说上图理解!
有这样一个链表,除去表头就是a1,a2,a3对吧!如果我们要逆序,逆序完成应该是a3,a2 a1。我们要如何逆序呢?
便于观察我们简写一下链表格式:
表头 a1 a2 a3 a4 a5 a6 a7.......
如果我们将a2提出以来,a1和a3相连是不是就是
表头 a2 a1 a3 a4 a5 a6 a7.......
如果我们把a3提出来,a1,a4相连是不是就是:
表头 a3 a2 a1 a4 a5 a6 a7.......
我们在多写几个
提出a4,a1和a5相连是不是就是
表头 a4 a3 a2 a1 a5 a6 a7.......
看起来是不是很简单呢,一步步提出再连接是不是不就逆序完成了嘛。
接下来到重点了,代码实现。
先看一下链表节点结构
typedef struct lianbiao
{
int date;
struct lianbiao *pnext;
}link;
link *phead;//头节点
首先我们是不是要打断a2嘛,所以我们是不是要先保存a2的地址。
我们要怎样表示a2的地址呢?
这里再提一下链表的逆序,表头不变。
设表头地址是L
,我们在增加一个变量current
current=L->pnext;
这个是不是a1的地址,存在表头的指针域中!
哪a2的地址是不是就是current->pnext
,既然我们要保存a2的地址是不是还要在增加一个参数,我们就在增加一个参数p
a2的地址是不是就是
p=current->pnext
这样我们就保存了a2的地址,然后我们让a1,a3相连
current->pnext=p->pnext
这样是不是a1,a3连起来了
接下来我们是不是需要把表头,a2,a1连起来,不就构成 L->a2->a1->a3…了嘛
我们在实现a2->a1->a3…
p->pnext=L->pnext
这是不是a2连上了a1,就剩下了一个表头和a2了嘛,我们在连一下
L->pnext=p
这样不就成功了第一步了嘛 L->a2->a1->a3…
p=current->pnext
current->pnext=p->pnext
p->pnext=L->pnext
L->pnext=p
就这样四句代码,是不是很简单呀!
我们继续下去
L->a2->a1->a3->a4->a5->a6......
再次看一下哪四句代码,第一句是不是保存a2的地址为p
但是执行完第二句a1连接a3的操作后,current->pnext
是不是保存的是a3的地址了?注意,a2的地址已经保存在参数p中了。如果我们再次执行一遍这四个语句
p=current->pnext
执行完后p是不是就是a3的地址了,他是不是存放在a1的指针域里面。
current->pnext=p->pnext
再执行完这一句你会发现是不是a1和a4连接了现在是不是就是
表头 a2 a1 a4 提出来了a3
是不是只用再实现a3和表头,a2相连就构成了
表头 a3 a2 a1.....
p->pnext=L->pnext
执行完这一句是不是a2的地址放在了a3的指针域里面,实现了
a3 a2 a1.....
就差一个表头了
L->pnext=p
这样是不是就构成了表头 a3 a2 a1.....
回头看一下执行完第二句子后,p的数值是不是又变成了a4的地址
我们是不是提出a4,a1,a5相连,再连表头是不是又变成了
表头 a4 a3 a2 a1 a5.......
写到这里你这会应该发现不是只要循环执行哪四个语句就行了
害!!!累死我了
p的地址是不是一直再变,a2,a3,a4…我们可以把这个作为循环判断条件把!我们给封装成一个函数
void turn(link *L)
{
link *current,*p;
current=L->pnext;
while(current->pnext!=NULL)
{
p=current->pnext;//保存a2地址
current->pnext=p->pnext;//a1连a3,孤立a2
p->pnext=L->pnext;//a2连a1
L->pnext=p;//a2连头节点
}
}
到此!结束!!!
附上完整程序代码:
#include <stdio.h>
#include <stdlib.h>
typedef struct lianbiao
{
int date;
struct lianbiao *pnext;
}link;
link *pnew;//新节点
link *phead;//头节点
link *ptemp;//临时节点
link *pdel;//删除功能用
link *pins;//插入功能用
int count=1;
void creat(void);
void add(void);
void del(void);
void ins(void);
void turn(link *L);
int main()
{
int n,i;
printf("请输入所需要创建的链表长度,不可为0\n");
scanf("%d", &n);
creat();
for(i=0;i<n-1;i++)
{
add();
}
printf("链表创建完毕,输出链表所有数据\n");
//printf("%d ", phead->date);
for(ptemp=phead;ptemp!=NULL;ptemp=ptemp->pnext)
{
printf("%d ", ptemp->date);
}
ins();
del();
printf("删除后的链表数据\n");
ptemp=phead;
while(ptemp!=NULL)
{
printf("%d ", ptemp->date);
ptemp=ptemp->pnext;
}
printf("\n");
printf("链表逆序:\n");
turn(phead);
return 0;
}
void creat(void)
{
phead=(link*)malloc(sizeof(link));
phead->pnext=NULL;
phead->date=0;
//memset(link,0,sizeof(link));
}
void add(void)
{
pnew=(link*)malloc(sizeof(link));
pnew->date=count;
pnew->pnext=NULL;
ptemp=phead;
while(ptemp->pnext!=NULL)
{
ptemp=ptemp->pnext;
}
ptemp->pnext=pnew;
//pnew=NULL;
count++;
}
void del(void)
{
int x;
int flag=0;
printf("\n");
printf("请输入你要删除的数\n");
scanf("%d", &x);
ptemp=phead;
while(ptemp->pnext!=NULL)
{
if(ptemp->pnext->date==x)
{
flag=1;
break;
}
ptemp=ptemp->pnext;
}
pdel=ptemp->pnext;
if(flag==1)
{
ptemp->pnext=pdel->pnext;
free(pdel);
}
}
void ins(void)
{
int y;
int flag=0;
printf("\n");
printf("请输入你要插入的数的位置\n");
scanf("%d", &y);
ptemp=phead;
while(ptemp->pnext!=NULL)
{
if(ptemp->pnext->date==y)
{
flag=1;
break;
}
ptemp=ptemp->pnext;
}
if(flag=1)
{
printf("已找到插入位置\n");
}
pnew=(link*)malloc(sizeof(link));
pnew->date=99;
pins=ptemp->pnext;//保存·后一个节点地址
ptemp->pnext=pnew;
pnew->pnext=pins;
printf("插入完成,输出所有数据\n");
ptemp=phead;
while(ptemp!=NULL)
{
printf("%d ", ptemp->date);
ptemp=ptemp->pnext;
}
}
//逆序头节点不变
void turn(link *L)
{
link *current,*p;
current=L->pnext;
while(current->pnext!=NULL)
{
p=current->pnext;//保存a2地址
current->pnext=p->pnext;//a1连a3,孤立a2
p->pnext=L->pnext;//a2连a1
L->pnext=p;//a2连头节点
//每循环一次,输出一次逆序结果
ptemp=phead;
while(ptemp!=NULL)
{
printf("%d ", ptemp->date);
ptemp=ptemp->pnext;
}
printf("\n");
}
}
运行结果
我再链表头节点的数据域里面也存了数据,输出也包括头节点。不是错误哟!
感谢评阅,如有问题可以在评论区或者私信我!欢迎指正交流。qq:918619587