1、链表的概念
链表是一种物理存储上非连续,数据元素的逻辑顺序通过链表中的指针链接次序,实现的一种线性存储结构
链表由一些列节点(链表中每一个元素称为节点)组成,节点在运行时动态生成(malloc),每个节点包括两部分:
1、存储数据元素的数据域
2、存储下一个节点地址的指针域
2、链表节点
3、链表的定义
typedef struct stu
{
//数据域
int num;
char name[32];
float score;
//指针域,next保存下一个节点的地址编号
struct stu *next;
}
例如
#include <stdio.h>
typedef struct stu
{
//数据域
int num;
char name[32];
float score;
//指针域
//next保存下一个节点的地址 编号
struct stu *next;
}STU;
void test01()
{
STU data1={100,"lucy",89.1,NULL};
STU data2={101,"lucy1",89.1,NULL};
STU data3={102,"lucy2",89.1,NULL};
STU data4={103,"lucy3",89.1,NULL};
//链表头
STU *head = &data1;
data1.next = &data2;
data2.next = &data3;
data3.next = &data4;
//遍历
STU *pb = head;
while(pb != NULL)
{
printf("%d %s %f\n", pb->num, pb->name,pb->score);
//去往下一个节点
pb = pb->next;
}
}
int main(int argc, char const *argv[])
{
test01();
return 0;
}
对链表的操作包括 增、删、改、查;链表的功能十分强大,在复杂的工程中会有双线链表,循环链表;
4、链表的插入操作
1、头插法
就是字面意思,每一个新插入的节点都将成为链表的新头部。
STU* insert_link(STU *head, STU tmp)
{
//1、为插入的节点 申请 堆区空间
STU *pi = (STU *)calloc(1,sizeof(STU));
//2、将tmp的数据 赋值到 *pi中
*pi = tmp;
//将pi->next赋值为NULL
pi->next = NULL;
//3、判断链表是否为NULL
if(head == NULL)//链表不存在
{
head = pi;
//return head;
}
else//链表存在
{
//让pi->next指向旧的head节点
pi->next = head;
//更新头节点 信息
head = pi;
//return head;
}
return head;
}
2、尾插法
每一个新节点都将成为链表的尾部。
//尾部插入
STU* insert_link(STU *head, STU tmp)
{
//1、为带插入的节点 申请空间
STU *pi = (STU *)calloc(1,sizeof(STU));
//2、将tmp的数据 赋值给*pi
*pi = tmp;
pi->next = NULL;
//3、判断链表 是否存在
if(head == NULL)//不存在
{
head = pi;
//return head;
}
else//存在
{
//寻找尾节点
STU *pb = head;
while(pb->next != NULL)
{
pb = pb->next;
}
//将尾节点的 指针域next 指向新的pi节点
pb->next = pi;
//return head;
}
return head;
}
3、链表的有序插入
//链表的有序插入
STU* insert_link(STU *head, STU tmp)
{
//1、为待插入的节点 申请空间
STU *pi = (STU *)calloc(1,sizeof(STU));
//2、将tmp的数据 赋值给*pi
*pi = tmp;
pi->next = NULL;
//判断链表 是否为空
if(head == NULL)//为NULL
{
head = pi;
return head;
}
else//不为空
{
//逐个节点寻找插入点的位置
STU *pf = head, *pb = head;
//pb->num < pi->num 按照num从小-->大排
//pb->next != NULL 防止链表 查询越界
while((pb->num < pi->num) && (pb->next != NULL))
{
//pf记录pb移动之前的位置
pf = pb;
//pb往下一个节点移动
pb = pb->next;
}
//判断插入点的位置(前 中 尾)
if(pb->num >= pi->num)//前 中
{
if(pb == head)//头部前 插入
{
pi->next = head;//保存旧链表的头地址
head = pi;//跟新新链表头地址
}
else//中部 插入
{
pf->next = pi;
pi->next = pb;
}
}
else if(pb->next == NULL)//尾部
{
pb->next = pi;
}
}
return head;//重要
}
4、链表的遍历
void print_link(const STU *head)
{
//1、判断链表 是否存在
if(head == NULL)//不存在
{
printf("链表不存在\n");
return;
}
else//存在
{
const STU *pb = head;
while(pb != NULL)
{
printf("num=%d, name=%s, score=%f\n", pb->num, pb->name, pb->score);
pb = pb->next;
}
}
}
5、链表的查找
STU* find_link(STU *head, char *name)
{
//1、判断链表是否存在
if(head == NULL)
{
printf("链表不存在\n");
return NULL;
}
else//链表存在
{
//逐个节点查询
STU *pb = head;
while((strcmp(pb->name, name) != 0) && (pb->next != NULL))
{
pb = pb->next;
}
//判断越界 还是找到相应的节点
if(strcmp(pb->name, name) == 0)//找到
{
return pb;
}
else if(pb->next == NULL)//未找到
{
printf("未找到姓名为%s的节点信息\n", name);
return NULL;
}
}
return NULL;
}
6、删除链表指定节点
STU* delete_link(STU *head,float score)
{
//判断链表是否存在
if(head == NULL)//不存在
{
printf("链表不存在\n");
return head;
}
else//存在
{
//逐个节点查询删除点
STU *pf = head, *pb = head;
while((pb->score != score) && (pb->next != NULL))
{
pf = pb;
pb = pb->next;
}
if(pb->score == score)//找到删除点
{
if(pb ==head)//删除头节点
{
head = head->next;
}
else//删除中、尾节点
{
pf->next = pb->next;
}
//释放要删除的节点
free(pb);
printf("节点删除成功\n");
return head;
}
else//未找到删除点
{
printf("未找到删除点\n");
return head;//重要
}
}
return head;
}
7、释放整个链表
while (pb! =NULL)
{
//先记录下一个节点的位置head = head->next;
//释放pb的节点free(pb);
//更新pb的位置pb = head;
}
STU *free_link(STU *head)
{
//判断链表 是否存在
if(head == NULL)
{
printf("链表不存在\n");
return head;
}
else//存在
{
STU *pb = head;
//逐个节点释放
while(pb != NULL)
{
//head记录下一个节点的位置
head = head->next;
//释放pb
free(pb);
//更新pb的位置
pb = head;
}
return head;
}
return head;
}
8、链表的逆序
STU* reverse_link(STU *head)
{
//判断链表是否存在
if(head == NULL)
{
printf("链表不存在\n");
return head;
}
else
{
STU *pb = NULL, *pr = NULL;
//pb的初始值
pb = head->next;
//将head->next置NULL
head->next = NULL;
//逐个节点逆置
while(pb != NULL)
{
//使用pr记录pb->next;
pr = pb->next;
//更改pb->next;
pb->next = head;
//更新head
head = pb;
//更新pb
pb = pr;
}
}
return head;
}
9、双向链表
双向链表可以更灵活的 进行数据的增删改查,后续在进行详细总结。