单链表的创建
结点创建:我们假设要创建的链表的数据域用来存储学生信息,即姓名,学号和成绩:
typedef struct student
{
char name[20];
int num,score;
struct student *next; //指向下一个结点的指针
}stu;
接下来先用尾插法(即新增结点在尾部插入)创建链表:
#include <stdio.h> #include <stdlib.h> typedef struct student { char name[20]; int num,score; struct student *next; }stu; stu *creat(int n) //n作为被传递的参数代表要创建的链表的结点个数,函数返回值类型是结点指针类型 { stu *head,*p,*q; //p,q为两个临时指针 int i; if(n>0) { head=(stu *)malloc(sizeof(stu)); //为head分配动态内存空间 printf("请输入姓名,学号,分数:"); scanf("%s %d %d",head->name,&head->num,&head->score); p=head; //让p和head指向相同 for(i=1;i<n;i++) { q=(stu *)malloc(sizeof(stu)); //再建一个结点,用q指向它 printf("请输入姓名,学号,分数:"); scanf("%s %d %d",q->name,&q->num,&q->score); p->next=q; //p的指针域存q p=q; } p->next=NULL; //循环外让p的指针域为空,因为这是最后一个结点
} else head=NULL; return head; //返回头指针 } int main() { int n; printf("输入链表结点的个数:"); scanf("%d",&n); stu *head; head=creat(n); //head接受函数返回值,保存链表起始的地址 if(n) { printf("姓名\t学号\t分数\n"); printf("-------------------\n"); for(int i=0;i<n;i++) { printf("%s\t%d\t%d\n",head->name,head->num,head->score); head=head->next; } } else printf("这是一个空链表!"); //n为0时链表没有结点 }
自己写的代码可能有许多不足之处,不过意思应该差不多了
下面看一下头插法(即新增结点总是插入头部):
#include <stdio.h>
#include <stdlib.h>
typedef struct student
{
char name[20];
int num,score;
struct student *next;
}stu;
stu *creat(int n)
{
stu *head,*p; //p为临时指针
int i;
if(n>0)
{
head=(stu *)malloc(sizeof(stu)); //为head分配动态内存空间
printf("请输入姓名,学号,分数:");
scanf("%s %d %d",head->name,&head->num,&head->score);
head->next=NULL; //让head的指针域为空
for(i=1;i<n;i++)
{
p=(stu *)malloc(sizeof(stu)); //为p分配动态内存空间
printf("请输入姓名,学号,分数:");
scanf("%s %d %d",p->name,&p->num,&p->score);
p->next=head; //让p指向head
head=p; //将head移动到p的指向上
}
}
else
head=NULL;
return head;
}
int main()
{
int n;
printf("输入链表结点的个数:");
scanf("%d",&n);
stu *head;
head=creat(n);
if(n)
{
printf("姓名\t学号\t分数\n");
printf("-------------------\n");
for(int i=0;i<n;i++)
{
printf("%s\t%d\t%d\n",head->name,head->num,head->score);
head=head->next;
}
}
else
printf("这是一个空链表!");
}
这两种方式创建的链表结点会刚好是反向的:
例如,如果运行上面两段程序,结果如下:
输入链表节点的个数:3
请输入姓名,学号,分数:张三 11 78
请输入姓名,学号,分数:李四 12 85
请输入姓名,学号,分数:王五 13 92
请输入姓名,学号,分数:张三 11 78
请输入姓名,学号,分数:李四 12 85
请输入姓名,学号,分数:王五 13 92
第一段:
姓名 学号 分数
-------------------
张三 11 78
李四 12 85
王五 13 92
姓名 学号 分数
-------------------
张三 11 78
李四 12 85
王五 13 92
第二段:
姓名 学号 分数
-------------------
王五 13 92
李四 12 85
张三 11 78
单链表的遍历
其实在上面两段代码的主函数中已经有这种思想了,只是并没有作介绍,我们把这种思想提出来单独看一下,以和上面结构一样的链表为例,写一个遍历数据并输出的子函数:
void Print(stu *h) //h做函数参数,这里将要接收的是指向链表首结点的指针
{
stu *t=h; //让临时指针得到首结点地址
int i=1;
while(t!=NULL)
{
printf("第%d个学生信息:\n",i);
printf("姓名\t学号\t分数\n");
printf("-------------------\n");
printf("%s\t%d\t%d\n\n",t->name,t->num,t->score);
t=t->next; //让临时指针移动到下一个结点
i++;
}
}
单链表的插入
链表插入的主要思想就是先找到插入位置,然后让新增结点的指针域指向插入位置的下一个结点(暂时叫a),然后让原来a前面的结点的指针域指向待插入结点就完成了,看代码,依旧用上面创建的链表:
第一种情况,在链表首部插入新结点:
stu *Insert1(stu *h,int *pn)
/*h做函数参数,这里将要接收的是指向链表首结点的指针,pn是指向n的指针,下面要用到它改变n的值以统计新链表结点个数*/
{
stu *pnew; //要插入的新结点
pnew=(stu *)malloc(sizeof(stu));
printf("请输入姓名,学号,分数:");
scanf("%s %d %d",pnew->name,&pnew->num,&pnew->score);
pnew->next=h; //新结点指向原链表的首结点
h=pnew; //头指针指向新结点
*pn++; //新链表结点个数增加
return h; //返回新的头指针
}
第二种,在尾部插入:
stu *Insert2(stu *h,int *pn)
/*h做函数参数,这里将要接收的是指向链表首结点的指针
pn是指向n的指针,下面要用到它改变n的值以统计新链表结点个数*/
{
stu *pnew; //要插入的新结点
stu *p=h; //临时指针
while(p && p->next!=NULL)
p=p->next; //让p指在原链表的尾结点上
pnew=(stu *)malloc(sizeof(stu));
printf("请输入姓名,学号,分数:");
scanf("%s %d %d",pnew->name,&pnew->num,&pnew->score);
p->next=pnew; //原链表的尾结点的指针指向新结点
pnew->next=NULL; //新结点指针域为空,成为新链表的尾结点
*pn++; //新链表结点个数增加
return h; //返回头指针
}
第三种,在首尾之间插入,假设要在学号为number的学生的位置后插入:
stu *Insert3(stu *h,int *pn,int number)
/*h做函数参数,这里将要接收的是指向链表首结点的指针
pn是指向n的指针,下面要用到它改变n的值以统计新链表结点个数*/
{
stu *pnew; //要插入的新结点
stu *p=h; //临时指针
while(p && p->num!=number)
p=p->next; //让p指在原链表中的存储指定学号学生信息的节点的前一个结点上
pnew=(stu *)malloc(sizeof(stu));
printf("请输入姓名,学号,分数:");
scanf("%s %d %d",pnew->name,&pnew->num,&pnew->score);
pnew->next=p->next; //新结点的指针指向原链表的插入结点的下一个结点
p->next=pnew; //插入结点的指针指向新结点
*pn++; //新链表结点个数增加
return h; //返回头指针
}
单链表的删除
删除也是在遍历的基础上进行操作的,思想就是先找到要删除的结点,再让它前面一个结点的指针域指向它的后面一个结点,还是用同一个链表,假设要删除存储学号为number的学生信息的结点:
stu *Delete(stu *h,int *pn,int number)
/*h做函数参数,这里将要接收的是指向链表首结点的指针
pn是指向n的指针,下面要用到它改变n的值以统计新链表结点个数*/
{
stu *t=h; //临时指针
stu *pPre=t; //要删除结点的前一个结点
while(p && p->num!=number)
p=p->next; //让p指在原链表中的存储指定学号学生信息的节点的前一个结点上
pPre=p; //指向待删除结点的前一个结点
p=p->next; //现在p指向待删除结点
pPre->next=t->next; //连接待删除结点两边结点
free(t) //释放待删除结点的内存空间
*pn--; //新链表结点个数减少
return h; //返回头指针
}
单链表的基本操作就这些啦,分享到此结束。