目录
单链表
概述: 单链表是一种常见的线性数据结构,用于存储一系列元素。单链表中的每个元素被称为节点(Node),每个节点包含两个部分:数据域(存储数据)和指针域(指向下一个节点的指针)。
单链表中的第一个结点的存储位置叫做头指针,规定最后一个节点的指针为空。有时候为了方便对链表操作,会在链表的第一个结点前加一个头节点,头结点可以不存储任何信息。
1 单链表的结构
概述: 单链表的结构为结构体,结构体中有两个元素,一个存放当前的数据,另一个为结构体指针保存的是下一节点的地址
// 单链表结构模型
struct list
{
int data; // 存储数据
struct list* next; // 存储下一节点的地址
};
2 创建单链表
概述:在创建单链表的时候一般会先创建一个头结点,然后利用头结点去创建单链表,头结点的数据域一般忽略,指针域为空。
// 创建头结点 并初始化
struct list head;
head.next = NULL;
void Creatlist(struct list* head, int n)
{
// 创建 尾结点指针 当做 中间变量来 操作节点
struct list* tail = (struct list *) malloc(sizeof (struct list));
// tail 是指针 指向的是 头节点
tail = head; // 头节点指针赋值给尾节点 此时 通过tail 可控制头节点的数据
for (int i = 0; i < n; ++i)
{
// 创建新节点
struct list* temp = (struct list*)malloc(sizeof(struct list));
// 给新节点赋值
// 初始化节点的指针 和 数据
temp->next = NULL;
temp->data = i + 1;
// 尾节点的指针域 指向下一节点
// 第一次时 tail 操作的是 头节点的下一节点 指向新节点
// 第二次...时 tail 操作的是 上次的节点 把它的下一节点指向 新节点
tail->next = temp;
// 新加入了一个节点 此时的尾结点 应该是新的节点
// 所以把 temp 赋值给tail 即更新 tail 操作的对象 此时 tail 操作新赋值的对象
// 且第一次时 tail 放弃对 head 的操作权
// 且第二...此时 tail放弃对 上一节点的操作权
tail = temp;
// 把尾节点 移动到最后一个 此时 尾结点的的数据为? 指针域为?
// 数据为i 指针域为NULL 因为 tail 指向的是temp
}
}
上例先创建一个头节点,然后在函数中用for循环对这个头结点连续插入结点。 要注意在对单链表进行操作的时候,会创建一个指针指向它的头结点然后用这个指针对节点进行添加操作。
3 在单链表中插入数据 e
思路:
-
创建新结点e
-
找到要插入的位置结点p
-
把p结点的下一结点地址p->next 的值赋值给 e结点的下一结点的 e->next
-
再把e的地址赋值给p节点的指针域存入即 p->next = e;
// 在单链表中插入数值
// 参数1 传入单链表指针
// 参数2 插入的位置
// 参数3 插入的数值
void InsetList(struct list* head, int pos, int num)
{
// 创建新节点
struct list* new_node = (struct list*)malloc(sizeof(struct list));
new_node->data = num;
struct list* temp = head; // 创建工作结点 指向头结点
for (int i = 0; i < pos - 1 && temp != NULL; i++) // 从头结点遍历到要插入的结点位置
{
temp = temp->next;
}
if (temp == NULL)
{
printf("插入失败,插入位置越界\n");
free(new_node); // 释放动态分配的内存
return;
}
new_node->next = temp->next; // 把前一结点的下一结点 赋值给新节点的下一结点
temp->next = new_node; // 把新结点的地址赋值给前一节点
}
注意: 插入的时候赋值顺序不能反,要先把p的后继节点赋值给e的后继,再把e赋值给p的后继。
3 删除单链表中第i个结点
思路:
-
声明一指针指向头结点 struct list* temp = head;
-
遍历 temp 到要删除的结点的前一个结点
-
创建指针指向要删除的结点 struct list* dele = temp->next;
// 删除单链表中的节点
// 参数1 传入单链表头结点指针
// 参数2 要删除的位置
void Delet_List(struct list* head, int pos)
{
if (head->next == NULL)
{
printf("链表为空,无法删除\n");
return;
}
struct list* temp = head; // 创建临时结点指向头结点
for (int i = 0; i < pos - 1 && temp->next != NULL; i++) // 遍历结点到指定的位置
{
temp = temp->next;
}
if (temp->next == NULL) // temp->为要删除的结点 若为空则说明结点不存在 则不能删除
{
printf("删除失败,删除位置越界\n");
return;
}
struct list* dele = temp->next; // temp为要删除的前一结点 temp->next 为要删除的结点
temp->next = dele->next; // 把要删除的结点的下一节点赋值给 前一结点的下一结点
free(dele); // 释放删除节点的内存
}
4 清空单链表
思路: 从头指针开始直到空指针 一个一个删除
// 删除整个单链表
// 参数1 单链表 头指针
void DELET(struct list* head)
{
// 定义工作指针current
struct list* current = head->next;
while (current != NULL)
{
// 保存当前的下一个指针域
struct list* next = current->next;
// 删除当前指针
free(current);
// 把前面保存的下一个指针 赋值给当前
current = next;
}
// 删除完链表 也要把头指针置空
head->next = NULL;
}
5 完整代码及运行结果
#include <stdio.h>
#include <stdlib.h>
struct list
{
int data;
struct list* next;
};
// 创建单链表
// 参数1 传入头结点指针
// 参数2 链表长度
void CreatList(struct list* head, int n)
{
struct list* tail = head;
for (int i = 0; i < n; ++i)
{
struct list* new_node = (struct list*)malloc(sizeof(struct list));
new_node->data = i + 1;
new_node->next = NULL;
tail->next = new_node;
tail = new_node;
}
}
// 在单链表中插入数值
// 参数1 传入单链表指针
// 参数2 插入的位置
// 参数3 插入的数值
void InsetList(struct list* head, int pos, int num)
{
// 创建新节点
struct list* new_node = (struct list*)malloc(sizeof(struct list));
new_node->data = num;
struct list* temp = head;
for (int i = 0; i < pos - 1 && temp != NULL; i++)
{
temp = temp->next;
}
if (temp == NULL)
{
printf("插入失败,插入位置越界\n");
free(new_node); // 释放动态分配的内存
return;
}
new_node->next = temp->next;
temp->next = new_node;
}
// 删除单链表中的节点
// 参数1 传入单链表头结点指针
// 参数2 要删除的位置
void Delet_List(struct list* head, int pos)
{
if (head->next == NULL)
{
printf("链表为空,无法删除\n");
return;
}
struct list* temp = head;
for (int i = 0; i < pos - 1 && temp->next != NULL; i++)
{
temp = temp->next;
}
if (temp->next == NULL)
{
printf("删除失败,删除位置越界\n");
return;
}
struct list* dele = temp->next;
temp->next = dele->next;
free(dele); // 释放删除节点的内存
}
// 遍历单链表
// 参数1 单链表头结点指针
void PrintfList(struct list* head)
{
if (head->next == NULL)
{
printf("链表为空\n");
return 0;
}
struct list* crunt = head->next; // 直接声明指针,无需动态分配内存
for (crunt; crunt != NULL; crunt = crunt->next)
{
printf("%d\t", crunt->data);
}
printf("\n");
}
// 删除整个单链表
// 参数1 单链表 头指针
void DELET(struct list* head)
{
// 定义工作指针current
struct list* current = head->next;
while (current != NULL)
{
// 保存当前的下一个指针域
struct list* next = current->next;
// 删除当前指针
free(current);
// 把前面保存的下一个指针 赋值给当前
current = next;
}
// 删除完链表 也要把头指针置空
head->next = NULL;
}
int main()
{
// 创建头结点
struct list head;
head.next = NULL;
// 创建单链表
CreatList(&head, 10);
// 遍历单链表
PrintfList(&head);
// 在单链表中插入数值
InsetList(&head, 3, 666);
// 遍历单链表
PrintfList(&head);
// 在单链表中插入数值
InsetList(&head, 1, 777);
// 遍历单链表
PrintfList(&head);
// 删除节点
Delet_List(&head, 16);
// 遍历单链表
PrintfList(&head);
// 释放整个链表
DELET(&head);
PrintfList(&head);
return 0;
}
结果
1 2 3 4 5 6 7 8 9 10
1 2 666 3 4 5 6 7 8 9 10
777 1 2 666 3 4 5 6 7 8 9 10
删除失败,删除位置越界
777 1 2 666 3 4 5 6 7 8 9 10
链表为空
基于C语言单链表的图书管理系统
要求: 输入1 添加书本 ,输入2根据ID删除书本,输入3根据ID 查看书本,输入4查看全部
图书管理系统是对链表的添加删除查找等功能的使用,在学习完链表后可以自己尝试做一个图书管理系统。
图书管理系统完整代码
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
// 定义书的链表结构
typedef struct BOOK
{
char name[100];
char author[100];
int price;
int id;
struct BOOK* next;
} BOOK;
int main(void)
{
// 创建书的头结点
BOOK head;
head.next = NULL;
BOOK* tail = &head;
while (1)
{
printf("***********************************************\n");
printf("1,添加书本\t2, 删除书本\t3, 查看\t4, 查看全部\n");
printf("请输入选项: \t");
int chose = 0;
scanf_s("%d", &chose);
if (chose == 1)
{
// 创建新书
BOOK* book = (BOOK*)malloc(sizeof(BOOK));
// 给这本书赋值
printf("请输入书名名字:\t ");
scanf("%s", book->name);
printf("\n请输入作者:\t ");
scanf("%s", book->author);
printf("\n请输入ID:\t ");
scanf("%d", &book->id);
printf("\n请输入价格:\t ");
scanf("%d", &book->price);
// 把这本书与前面的节点连接起来
tail->next = book;
// 移动尾结点到这本书的位置
tail = book;
// 置空尾结点便于后续操作
tail->next = NULL;
}
else if (chose == 2)
{
printf("请输入要删除的ID\n");
int del = 0; // 接收删除的ID
scanf("%d", &del);
BOOK* pre = &head; // 定义指向头的节点
BOOK* temp; // 定义工作节点
int flog = 0;
for (temp = pre->next; temp != NULL; pre = temp, temp = temp->next)
{
if (del == temp->id)
{
flog = 1;
pre->next = temp->next;
free(temp);
break;
}
}
if (flog == 0)
{
printf("ID 不存在\n");
}
else if (flog == 1)
{
printf("删除成功\n");
}
}
else if (chose == 3)
{
printf("请输入要查看的ID\n");
int look = 0;
scanf("%d", &look);
int flog1 = 0;
for (BOOK* temp = head.next; temp != NULL; temp = temp->next)
{
if (look == temp->id)
{
flog1 = 1;
printf("书名:%s\t\n作者:%s\t\nID:%d\t\n价格:%d\t\n", temp->name, temp->author, temp->id, temp->price);
break;
}
}
if (flog1 == 0)
{
printf("查无次书\n");
}
}
else if (chose == 4)
{
printf("书名\t作者\tID\t价格\n");
for (BOOK* temp = head.next; temp != NULL; temp = temp->next)
{
printf("%s\t%s\t%d\t%d\n", temp->name, temp->author, temp->id, temp->price);
}
}
else
printf("输入错误\n");
}
return 0;
}
结果
***********************************************
1,添加书本 2, 删除书本 3, 查看 4, 查看全部
请输入选项: 1
请输入书名名字: 数据结构
请输入作者: 张三
请输入ID: 1
请输入价格: 20
***********************************************
1,添加书本 2, 删除书本 3, 查看 4, 查看全部
请输入选项: 1
请输入书名名字: 算法
请输入作者: 李四
请输入ID: 2
请输入价格: 33
***********************************************
1,添加书本 2, 删除书本 3, 查看 4, 查看全部
请输入选项: 1
请输入书名名字: C语言基础
请输入作者: 王五
请输入ID: 3
请输入价格: 44
***********************************************
1,添加书本 2, 删除书本 3, 查看 4, 查看全部
请输入选项: 4
书名 作者 ID 价格
数据结构 张三 1 20
算法 李四 2 33
C语言基础 王五 3 44
***********************************************
1,添加书本 2, 删除书本 3, 查看 4, 查看全部
请输入选项: 3
请输入要查看的ID
2
书名:算法
作者:李四
ID:2
价格:33
***********************************************
1,添加书本 2, 删除书本 3, 查看 4, 查看全部
请输入选项: 2
请输入要删除的ID
2
删除成功
***********************************************
1,添加书本 2, 删除书本 3, 查看 4, 查看全部
请输入选项: 4
书名 作者 ID 价格
数据结构 张三 1 20
C语言基础 王五 3 44
***********************************************
1,添加书本 2, 删除书本 3, 查看 4, 查看全部
请输入选项:
上一篇:C语言数据结构线性表
下一篇:明日更新