一、链表的概念
1.定义:
1.链表是一种物理存储上非连续,数据元素的逻辑顺序通过链表中的指针链接次序,实现的一种线性存储结构。
2.特点:链表由一系列节点(链表中每一个元素称为节点)组成,节点在运行时动态生成,每个节点包括两个部分:
一个是存储数据元素的数据域
另一个是存储下一个节点地址的指针域
2.链表的构成
链表由一个个节点构成,每个节点一般采用结构体的形式组织,例如
typedef struct student
{
int num;
char name[20];
struct student *next;
}STU;
链表节点分为两域
数据域:存放各种实际的数据,如:num、score等
指针域:存放下一节点的首地址,如: next 等
二、链表的创建
// 链表的构成
#include <stdio.h>
#include <stdlib.h>
typedef struct student
{
int num;
int scod;
char name[20];
struct student *next; // 指针域
} STU;
void link_creat_head(STU **p_head, STU *p_new)
{
STU *p_mov = *p_head;
if (*p_head == NULL)
{
*p_head = p_new;
p_new->next = NULL;
}
else
{
while (p_mov->next != NULL)
{
p_mov = p_mov->next;
}
p_mov->next = p_new;
p_new->next = NULL;
}
}
int main(void)
{
STU *head = NULL, *p_new = NULL;
int num, i;
printf("请输入链表初始个数:\n");
scanf("%d", &num);
for (i = 0; i < num; i++)
{
p_new = (STU *)malloc(sizeof(STU)); // 申请一个新节点
printf("请输入学号、分数、名字:\n");
scanf("%d %d %s", &p_new->num, &p_new->scod, p_new->name);
link_creat_head(&head, p_new);
}
}
三、链表的遍历
// 链表的遍历
void link_print(STU *head)
{
STU *p_mov;
p_mov = head;
while (p_mov != NULL)
{
printf("num=%d score=%d name:%s\n", p_mov->num, p_mov->scod, p_mov->name);
p_mov = p_mov->next; // 指针后移,保存下一个节点的地址
}
}
四、链表的释放
重新定义一个指针q,保存p指针结点的地址,然后p后移保存下一个结点的地址,然后释放q对应的结点,依次类推,直到p为NULL为止
// 链表的释放
void link_free(STU **p_head)
{
STU *pd = *p_head; // 定义一个指针变量保存头结点的地址
while (*p_head != NULL)
{
pd = *p_head; // 先保存p_head指向的结点的地址
*p_head = (*p_head)->next;
free(pd);
pd = NULL;
}
}
五、链表的查找
先对比第一个结点的数据域是否是想要的数据,如果是就直接返回,如果不是则继续查找下一个结点,如果到达最后一个结点的时候都没有匹配的数据,说明要查找数据不存在;
STU *link_search_num(STU *head, int num)
{
STU *p_mov;
// 定义的指针变量保存第一个结点的地址
p_mov = head;
while (p_mov != NULL)
{
if (p_mov->num == num) // 如果找到是当前结点的数据,则返回当前结点的地址
{
return p_mov;
}
p_mov = p_mov->next;
}
return NULL; // 没有找到
}
STU *link_search_name(STU *head, char *name)
{
STU *p_mov;
p_mov = head;
while (p_mov != NULL)
{
if (strcmp(p_mov->name, name) == 0)
{
return p_mov;
}
p_mov = p_mov->next;
}
return NULL; // 没有找到
}
六、链表节点的删除
如果链表为空,不需要删除
如果删除的第一个结点,则需要将保存链表首地址的指针保存第一个结点的下一个结点的地址
如果删除的是中间结点,则找到中间结点的前一个结点,让前一个结点的指针域保存这个结点的后一个结点的地址即可
// 链表结点的删除
void link_delete_num(STU **p_head, int num)
{
STU *pb, *pf;
pb = pf = *p_head;
if (*p_head == NULL)
{
printf("链表为空,没有您要删除的节点");
return ;
}
while (pb->num != num && pb->next != NULL)
{
pf = pb;
pb = pb->next;
}
if (pb->num == num) // 找到了一个节点的num和num相同
{
if (pb == *p_head) // 要删除的节点是头节点
{
*p_head = pb->next; // 让保存头结点的指针保存后一个结点的地址
}
else
{
pf->next = pb->next; // 前一个结点的指针域保存要删除的后一个结点的地址
}
free(pb);
}
else
{
printf("没有找到...");
}
}
七、链表中插入一个节点
链表中插入一个结点,按照原本链表的顺序插入,找到合适的位置
情况(按照从小到大):
1.如果链表没有结点,则新插入的就是第一个结点;
2.如果新插入的结点数值最小,则作为头结点;
3.如果新插入的结点的数值在中间位置,则找到前一个,然后插入到他们中间
4.如果新插入的结点的数值最大,则插入到最后
// 链表的插入:按照学号的顺序插入
void link_insert_num(STU **p_head, STU *p_new)
{
STU *pb, *pf;
pb = pf = *p_head;
if (*p_head == NULL)
{
*p_head = p_new;
p_new->next = NULL;
return ;
}
while ((p_new->num >= pb->num) && (pb->next != NULL))
{
pf = pb;
pb = pb->next;
}
if (p_new->num < pb->num) // 找到一个节点的num比新来的结点num大,插在pb前
{
if (pb == *p_head) // 找到的节点是头节点,插在最前面
{
p_new->next = *p_head;
*p_head = p_new;
}
else
{
pf->next = p_new;
p_new->next = pb;
}
}
else
{
pb->next = p_new;
p_new->next = NULL;
}
}
八、链表排序
如果链表为空,不需要排序
如果链表只有一个结点,不需要排序
先将第一个结点与后面所有的结点一次对比数据域,只要有比第一个结点数据域小的,则交换位置,交换之后,拿新的第一个结点的数据域与下一个结点再次对比,如果比他小,再次交换,一次类推
// 链表的排序
void link_rank_num(STU *head)
{
STU *pb, *pf, temp;
pf = head;
if (head == NULL)
{
printf("链表为空");
return ;
}
if (pf->next == NULL)
{
printf("链表只有一个值,不需要排序\n");
return ;
}
while (pf->next != NULL)
{
pb = pf->next;
while (pb != NULL)
{
if (pf->num > pb->num)
{
temp = *pb;
*pb = *pf;
*pf = temp;
temp.next = pb->next;
pb->next = pf->next;
pf->next = temp.next;
}
pb = pb->next;
}
pf = pf->next;
}
}
九、链表的逆序
// 链表逆序
STU *link_reverse(STU *head)
{
STU *pf,*pb,*r;
pf = head;
pb = pf->next;
while(pb != NULL)
{
r = pb->next;
pb->next = pf;
pf = pb;
pb = r;
}
head->next=NULL;
head = pf;
return head;
}
十、双向链表的创建和遍历
1.双向链表的创建
#include <stdio.h>
#include <stdlib.h>
//#define SINGLY_LINKED_LIST
#define DOUBLE_LINKED_LIST
#ifdef DOUBLE_LINKED_LIST
// 定义结点结构体
typedef struct student
{
// 数据域
int num;
int score;
char name[20];
// 指针域
struct student *front; // 保存上一个结点的地址
struct student *next; // 保存下一个结点的地址
} STU;
void double_link_creat_head(STU **p_head, STU *p_new)
{
STU *p_mov = *p_head;
if (*p_head == NULL)
{
*p_head = p_new;
p_new->front = NULL;
p_new->next = NULL;
}
else // 第二次及以后加入链表
{
while (p_mov->next != NULL)
{
p_mov = p_mov->next;
}
p_mov->next = p_new;
p_new->front = p_mov;
p_new->next = NULL;
}
}
void double_link_print(STU *head)
{
STU *p_mov;
p_mov = head;
while (p_mov->next != NULL)
{
printf("num=%d score=%d name:%s\n", p_mov->num, p_mov->score, p_mov->name);
p_mov = p_mov->next; // 指针后移,保存下一个节点的地址
}
printf("num=%d score=%d name:%s\n", p_mov->num, p_mov->score, p_mov->name);
printf("************************************************\n");
while (p_mov != NULL)
{
printf("num=%d score=%d name:%s\n", p_mov->num, p_mov->score, p_mov->name);
p_mov = p_mov->front; // 指针前移,保存下一个节点的地址
}
}
int main()
{
STU *head = NULL, *p_new = NULL;
int num, i;
printf("请输入链表初始个数:\n");
scanf("%d", &num);
for (i = 0; i < num; i++)
{
p_new = (STU *)malloc(sizeof(STU)); // 申请一个新节点
printf("请输入学号、分数、名字:\n");
scanf("%d %d %s", &p_new->num, &p_new->score, p_new->name);
double_link_creat_head(&head, p_new);
}
double_link_print(head);
return 0;
}
#endif
执行结果
2.双向链表节点的删除
如果链表为空,则不需要删除
如果删除第一个结点,则保存链表首地址的指针保存后一个结点的地址,并且让这个结点的front保存NULL;
如果删除最后一个结点,只需要让最后一个结点的前一个结点的next保存NULL即可;
如果删除中间结点,则让中间结点的前后两个结点的指针域分别保存对方的地址即可;
// 双向链表的删除
void double_link_delete_num(STU **p_head, int num)
{
STU *pb, *pf;
pb = *p_head;
if (*p_head == NULL)
{
printf("链表为空,没有您要删除的节点\n");
return ;
}
while ((pb->num != num) && (pb->next != NULL))
{
pb = pb->next;
}
if (pb->num == num) // 找到一个节点
{
if (pb == *p_head) // 找到的节点是头节点
{
if ((*p_head)->next == NULL) // 只有一个节点的情况
{
*p_head = pb->next;
}
else // 有多个节点的情况
{
*p_head = pb->next;
(*p_head)->front = NULL;
}
}
else
{
if (pb->next != NULL)
{
pf = pb->front;
pf->next = pb->next;
(pb->next)->front = pf;
}
else
{
pf = pb->front;
pf->next = NULL;
}
free(pb); // 释放找到的节点
}
}
else
{
printf("没有您要删除的节点\n");
}
}
3.双向链表插入节点
// 双向链表的插入
void double_link_insert_num(STU **p_head, STU *p_new)
{
STU *pb, *pf;
pb = *p_head;
if (*p_head == NULL) // 链表为空,新来的节点就是头节点
{
*p_head = p_new;
p_new->front = NULL;
p_new->next = NULL;
return ;
}
while ((p_new->num >= pb->num) && (pb->next != NULL))
{
pb = pb->next;
}
if (p_new->num < pb->num)
{
if (pb == *p_head) // 找到的节点是头结点,插在头节点的前面
{
p_new->next = *p_head;
(*p_head)->front = p_new;
p_new->front = NULL;
*p_head = p_new;
}
else
{
pf = pb->front; // pf指向 找到节点的前一个节点
p_new->front = pf;
p_new->next = pb;
pf->next = p_new;
pb->front = p_new;
}
}
else
{
pb->next = p_new;
p_new->front = pb;
p_new->next = NULL;
}
}
附录
// 链表的构成
#include <stdio.h>
#include <stdlib.h>
#include <cstring>
typedef struct student
{
int num;
int scod;
char name[20];
struct student *next; // 指针域
} STU;
// 链表的创建
void link_creat_head(STU **p_head, STU *p_new)
{
STU *p_mov = *p_head;
if (*p_head == NULL)
{
*p_head = p_new;
p_new->next = NULL;
}
else
{
while (p_mov->next != NULL)
{
p_mov = p_mov->next;
}
p_mov->next = p_new;
p_new->next = NULL;
}
}
// 链表的遍历
void link_print(STU *head)
{
STU *p_mov;
p_mov = head;
while (p_mov != NULL)
{
printf("num=%d score=%d name:%s\n", p_mov->num, p_mov->scod, p_mov->name);
p_mov = p_mov->next; // 指针后移,保存下一个节点的地址
}
}
// 链表的释放
void link_free(STU **p_head)
{
STU *pd = *p_head; // 定义一个指针变量保存头结点的地址
while (*p_head != NULL)
{
pd = *p_head; // 先保存p_head指向的结点的地址
*p_head = (*p_head)->next;
free(pd);
pd = NULL;
}
}
// 链表节点的查找
STU *link_search_num(STU *head, int num)
{
STU *p_mov;
// 定义的指针变量保存第一个结点的地址
p_mov = head;
while (p_mov != NULL)
{
if (p_mov->num == num) // 如果找到是当前结点的数据,则返回当前结点的地址
{
return p_mov;
}
p_mov = p_mov->next;
}
return NULL; // 没有找到
}
STU *link_search_name(STU *head, char *name)
{
STU *p_mov;
p_mov = head;
while (p_mov != NULL)
{
if (strcmp(p_mov->name, name) == 0)
{
return p_mov;
}
p_mov = p_mov->next;
}
return NULL; // 没有找到
}
// 链表结点的删除
void link_delete_num(STU **p_head, int num)
{
STU *pb, *pf;
pb = pf = *p_head;
if (*p_head == NULL)
{
printf("链表为空,没有您要删除的节点");
return ;
}
while (pb->num != num && pb->next != NULL)
{
pf = pb;
pb = pb->next;
}
if (pb->num == num) // 找到了一个节点的num和num相同
{
if (pb == *p_head) // 要删除的节点是头节点
{
*p_head = pb->next; // 让保存头结点的指针保存后一个结点的地址
}
else
{
pf->next = pb->next; // 前一个结点的指针域保存要删除的后一个结点的地址
}
free(pb);
}
else
{
printf("没有找到...");
}
}
// 链表的插入:按照学号的顺序插入
void link_insert_num(STU **p_head, STU *p_new)
{
STU *pb, *pf;
pb = pf = *p_head;
if (*p_head == NULL)
{
*p_head = p_new;
p_new->next = NULL;
return ;
}
while ((p_new->num >= pb->num) && (pb->next != NULL))
{
pf = pb;
pb = pb->next;
}
if (p_new->num < pb->num) // 找到一个节点的num比新来的结点num大,插在pb前
{
if (pb == *p_head) // 找到的节点是头节点,插在最前面
{
p_new->next = *p_head;
*p_head = p_new;
}
else
{
pf->next = p_new;
p_new->next = pb;
}
}
else
{
pb->next = p_new;
p_new->next = NULL;
}
}
// 链表的排序
void link_rank_num(STU *head)
{
STU *pb, *pf, temp;
pf = head;
if (head == NULL)
{
printf("链表为空");
return ;
}
if (pf->next == NULL)
{
printf("链表只有一个值,不需要排序\n");
return ;
}
while (pf->next != NULL)
{
pb = pf->next;
while (pb != NULL)
{
if (pf->num > pb->num)
{
temp = *pb;
*pb = *pf;
*pf = temp;
temp.next = pb->next;
pb->next = pf->next;
pf->next = temp.next;
}
pb = pb->next;
}
pf = pf->next;
}
}
// 链表逆序
STU *link_reverse(STU *head)
{
STU *pf,*pb,*r;
pf = head;
pb = pf->next;
while(pb != NULL)
{
r = pb->next;
pb->next = pf;
pf = pb;
pb = r;
}
head->next=NULL;
head = pf;
return head;
}
int main(void)
{
STU *head = NULL, *p_new = NULL;
int num, i;
printf("请输入链表初始个数:\n");
scanf("%d", &num);
for (i = 0; i < num; i++)
{
p_new = (STU *)malloc(sizeof(STU)); // 申请一个新节点
printf("请输入学号、分数、名字:\n");
scanf("%d %d %s", &p_new->num, &p_new->scod, p_new->name);
link_creat_head(&head, p_new);
}
link_print(head);
printf("******************************************\n");
link_rank_num(head);
link_print(head);
#if 0
// link_free(&p_new);
// link_print(head);
STU *pd;
while (1)
{
printf("请输入您要查找学生的学号:");
scanf("%d", &num);
pd = link_search_num(head, num);
if (pd != NULL)
{
printf("找到了\n");
}
else
{
printf("没有找到\n");
}
}
#elif 0
while (1)
{
printf("请输入您要删除的节点的学号\n");
scanf("%d", &num);
link_delete_num(&head, num);
link_print(head);
// link_free(&head);
}
#elif 0
while (1)
{
printf("请输入您要插入的节点的 num score name\n");
p_new = (STU *)malloc(sizeof(STU)); // 申请一个新的节点
scanf("%d %d %s", &p_new->num, &p_new->scod, p_new->name);
link_insert_num(&head, p_new);
link_print(head);
}
#else
printf("helle");
#endif
return 0;
}