













我们来一步步拆解这个练习,从理解结构到写出获取第一个学生 id 的完整代码,同时讲清边界处理和注意事项。
一、先理清代码结构
你提供的代码定义了两个核心类型:
struct student(学生类型) :存储单个学生的name(姓名)、id(学号)、age(年龄),并通过typedef重命名为datatype_t,方便后续顺序表使用。seqlist_t(顺序表类型) :用数组buf[MAX]存储最多MAX个学生信息,用n记录当前实际学生数量(有效元素个数)。
c
#define MAX 10
// 学生信息结构体
struct student
{
char name[20];
int id;
int age;
};
typedef struct student datatype_t;
// 顺序表结构体(存储班级学生)
typedef struct {
datatype_t buf[MAX]; // 学生数组(下标从0开始)
int n; // 实际学生数量
} seqlist_t;
二、如何定义 seqlist_t st 并获取第一个学生的 id?
核心原理
- 顺序表的
buf是数组,数组下标从 0 开始 ,因此第一个学生存储在buf[0]位置。 - 学生的
id是struct student的成员,因此访问方式为st.buf[0].id。
完整示例代码(含初始化、添加学生、获取id)
c
#include <stdio.h>
#include <string.h>
#define MAX 10
// 学生信息结构体
struct student
{
char name[20];
int id;
int age;
};
typedef struct student datatype_t;
// 顺序表结构体(存储班级学生)
typedef struct {
datatype_t buf[MAX]; // 学生数组(下标从0开始)
int n; // 实际学生数量
} seqlist_t;
int main()
{
// 1. 定义 seqlist_t 类型的变量 st
seqlist_t st;
st.n = 0; // 初始化学生数量为0(当前无学生)
// 2. 添加第一个学生(模拟录入数据)
strcpy(st.buf[0].name, "张三");
st.buf[0].id = 2025001; // 学号
st.buf[0].age = 18;
st.n = 1; // 更新实际学生数量为1
// 3. 安全获取第一个学生的 id(必须先判断学生数量,避免越界)
if (st.n > 0)
{
int first_student_id = st.buf[0].id;
printf("班级第一个学生的学号是:%d\n", first_student_id);
}
else
{
printf("班级还没有学生,无法获取id\n");
}
return 0;
}
三、关键细节说明
1. 为什么用 st.buf[0].id?
st是seqlist_t类型变量,st.buf是学生数组;- 数组下标从 0 开始,
st.buf[0]代表数组的第一个元素(第一个学生); .id是struct student的成员访问符,用于读取该学生的学号。
2. 必须先判断 st.n > 0
如果 st.n == 0(班级没有学生),直接访问 st.buf[0].id 会读取未初始化的内存,导致随机值或程序崩溃,因此必须先做边界判断。
3. 数组下标 vs 逻辑顺序
- 逻辑上的"第1个学生",对应数组下标
0; - 逻辑上的"第2个学生",对应数组下标
1; - 以此类推,第
i个学生的下标是i-1。
四、拓展:封装成函数(更规范的写法)
实际开发中,建议把"获取第一个学生id"封装成函数,复用性更高:
c
// 获取顺序表中第一个学生的id,成功返回id,失败返回-1
int get_first_student_id(seqlist_t *st)
{
if (st == NULL || st->n <= 0)
{
return -1; // 无学生或顺序表为空,返回-1表示失败
}
return st->buf[0].id;
}
// main函数中调用
int main()
{
seqlist_t st;
// ... 初始化并添加学生 ...
int id = get_first_student_id(&st);
if (id != -1)
{
printf("第一个学生id:%d\n", id);
}
return 0;
}
总结
获取第一个学生 id 的核心步骤:
- 定义
seqlist_t st; - 初始化并添加学生,确保
st.n >= 1; - 通过
st.buf[0].id访问学号,访问前必须判断学生数量,避免越界。


我帮你把课后任务的所有函数(创建、判满、尾插、输出)完整实现了,同时写好了main函数的测试用例,直接可编译运行。
一、完整代码实现
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 10
// 学生信息结构体
struct student
{
char name[20];
int id;
int age;
};
typedef struct student datatype_t;
// 顺序表结构体(存储班级学生)
typedef struct{
datatype_t buf[MAX]; // 存储学生信息的数组
int n; // 当前实际学生数量
}seqlist_t;
// 函数声明
seqlist_t *create_empty_seqlist();
int is_full_seqlist(seqlist_t *l);
void insert_data_seqlist(seqlist_t *l, datatype_t data);
void printf_data_seqlist(seqlist_t *l);
// 1. 创建空顺序表(动态分配内存)
seqlist_t *create_empty_seqlist()
{
seqlist_t *l = (seqlist_t*)malloc(sizeof(seqlist_t));
if (l == NULL)
{
perror("malloc failed"); // 打印内存分配失败原因
exit(1); // 异常退出程序
}
l->n = 0; // 初始学生数量为0
return l;
}
// 2. 判断顺序表是否已满
// 返回值:1=已满,0=未满,-1=顺序表不存在
int is_full_seqlist(seqlist_t *l)
{
if (l == NULL)
{
printf("错误:顺序表不存在\n");
return -1;
}
return (l->n == MAX) ? 1 : 0;
}
// 3. 向顺序表尾部插入学生数据
void insert_data_seqlist(seqlist_t *l, datatype_t data)
{
// 边界检查
if (l == NULL)
{
printf("错误:顺序表不存在,无法插入\n");
return;
}
if (is_full_seqlist(l) == 1)
{
printf("错误:顺序表已满,无法插入新学生\n");
return;
}
// 尾插:插入到当前最后一个元素的下一个位置
l->buf[l->n] = data;
l->n++; // 学生数量+1
}
// 4. 遍历输出顺序表中所有学生信息
void printf_data_seqlist(seqlist_t *l)
{
if (l == NULL)
{
printf("错误:顺序表不存在\n");
return;
}
if (l->n == 0)
{
printf("顺序表为空,暂无学生信息\n");
return;
}
printf("===== 班级学生信息列表 =====\n");
for (int i = 0; i < l->n; i++)
{
printf("第%d个学生:姓名:%-5s | 学号:%d | 年龄:%d\n",
i+1, l->buf[i].name, l->buf[i].id, l->buf[i].age);
}
printf("===========================\n");
}
// 主函数:测试所有功能
int main()
{
// 1. 创建空顺序表
seqlist_t *class_list = create_empty_seqlist();
// 2. 准备测试用的学生数据
datatype_t s1 = {"张三", 2025001, 18};
datatype_t s2 = {"李四", 2025002, 19};
datatype_t s3 = {"王五", 2025003, 18};
// 3. 插入学生数据
insert_data_seqlist(class_list, s1);
insert_data_seqlist(class_list, s2);
insert_data_seqlist(class_list, s3);
// 4. 输出所有学生信息
printf_data_seqlist(class_list);
// (可选)测试"判满"功能:插入超过MAX个元素
/*
for(int i=0; i<8; i++) // 再插8个,凑够11个(MAX=10)
{
datatype_t temp = {"临时学生", 2025004+i, 18};
insert_data_seqlist(class_list, temp);
}
printf_data_seqlist(class_list);
// 第11个会触发"已满"错误提示
datatype_t extra = {"额外学生", 2025012, 20};
insert_data_seqlist(class_list, extra);
*/
// 释放动态分配的内存,防止内存泄漏
free(class_list);
class_list = NULL;
return 0;
}
二、每个函数的作用说明
| 函数 | 核心功能 | 关键细节 |
|---|---|---|
create_empty_seqlist |
动态创建一个空的顺序表 | 用malloc分配内存,初始化学生数量n=0,失败时打印错误并退出 |
is_full_seqlist |
判断顺序表是否达到最大容量MAX |
同时处理"顺序表不存在"的异常,返回-1/0/1三种状态 |
insert_data_seqlist |
向顺序表尾部插入学生数据 | 先检查顺序表是否存在/已满,再将数据存入buf[n],并n++ |
printf_data_seqlist |
遍历输出所有学生的姓名、学号、年龄 | 先检查顺序表是否存在/为空,再循环打印每个元素 |
三、运行效果示例
===== 班级学生信息列表 =====
第1个学生:姓名:张三 | 学号:2025001 | 年龄:18
第2个学生:姓名:李四 | 学号:2025002 | 年龄:19
第3个学生:姓名:王五 | 学号:2025003 | 年龄:18
===========================
四、补充说明
- 尾插逻辑 :这里默认实现的是尾部插入 (新学生加在列表最后),如果需要指定位置插入,需要额外写一个
insert_pos_seqlist函数。 - 边界保护 :所有函数都做了
l == NULL的判断,防止空指针访问导致程序崩溃。 - 内存管理 :主函数最后用
free释放了动态分配的顺序表,避免内存泄漏。


我帮你把判空函数 和按学号删除学生的函数完整实现了,同时写好了可直接运行的测试代码。
一、完整代码实现
c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX 10
// 学生信息结构体
struct student
{
char name[20];
int id;
int age;
};
typedef struct student datatype_t;
// 顺序表结构体(存储班级学生)
typedef struct{
datatype_t buf[MAX]; // 存储学生信息的数组
int n; // 当前实际学生数量
}seqlist_t;
// 函数声明
int is_empty_seqlist(seqlist_t *l);
int delete_data_seqlist(seqlist_t *l, int id);
// (补充之前的基础函数,方便测试)
seqlist_t *create_empty_seqlist();
void insert_data_seqlist(seqlist_t *l, datatype_t data);
void printf_data_seqlist(seqlist_t *l);
// ---------------------- 判空函数 ----------------------
// 功能:判断顺序表是否为空
// 返回值:1=空表,0=非空,-1=顺序表不存在
int is_empty_seqlist(seqlist_t *l)
{
if (l == NULL)
{
printf("错误:顺序表不存在\n");
return -1;
}
return (l->n == 0) ? 1 : 0;
}
// ---------------------- 按id删除函数 ----------------------
// 功能:根据学号删除对应的学生
// 返回值:0=删除成功,-1=删除失败(表不存在/空表/未找到id)
int delete_data_seqlist(seqlist_t *l, int id)
{
// 1. 边界检查:顺序表是否存在、是否为空
if (l == NULL)
{
printf("错误:顺序表不存在,无法删除\n");
return -1;
}
if (is_empty_seqlist(l) == 1)
{
printf("错误:顺序表为空,没有学生可删除\n");
return -1;
}
// 2. 遍历数组,查找目标学号的下标
int i;
for (i = 0; i < l->n; i++)
{
if (l->buf[i].id == id)
{
break; // 找到目标,跳出循环
}
}
// 3. 如果遍历完都没找到,说明id不存在
if (i == l->n)
{
printf("错误:未找到学号为%d的学生\n", id);
return -1;
}
// 4. 找到后,将目标位置之后的元素全部往前移动,覆盖被删除的元素
for (int j = i; j < l->n - 1; j++)
{
l->buf[j] = l->buf[j + 1];
}
// 5. 学生数量减1(有效元素减少一个)
l->n--;
printf("✅ 成功删除学号为%d的学生\n", id);
return 0;
}
// ---------------------- 基础辅助函数(用于测试) ----------------------
seqlist_t *create_empty_seqlist()
{
seqlist_t *l = (seqlist_t*)malloc(sizeof(seqlist_t));
if (l == NULL) { perror("malloc failed"); exit(1); }
l->n = 0;
return l;
}
void insert_data_seqlist(seqlist_t *l, datatype_t data)
{
if (l == NULL || l->n >= MAX) return;
l->buf[l->n] = data;
l->n++;
}
void printf_data_seqlist(seqlist_t *l)
{
if (l == NULL || l->n == 0) { printf("顺序表为空\n"); return; }
printf("===== 学生列表 =====\n");
for (int i=0; i<l->n; i++)
{
printf("第%d个学生:姓名:%-5s | 学号:%d | 年龄:%d\n",
i+1, l->buf[i].name, l->buf[i].id, l->buf[i].age);
}
printf("====================\n");
}
// ---------------------- 主函数:测试所有功能 ----------------------
int main()
{
// 1. 创建空顺序表
seqlist_t *class_list = create_empty_seqlist();
// 2. 添加测试学生
datatype_t s1 = {"张三", 2025001, 18};
datatype_t s2 = {"李四", 2025002, 19};
datatype_t s3 = {"王五", 2025003, 18};
insert_data_seqlist(class_list, s1);
insert_data_seqlist(class_list, s2);
insert_data_seqlist(class_list, s3);
printf("删除前:\n");
printf_data_seqlist(class_list);
// 3. 测试删除学号2025002的学生
delete_data_seqlist(class_list, 2025002);
printf("删除后:\n");
printf_data_seqlist(class_list);
// 4. 测试删除不存在的学号
delete_data_seqlist(class_list, 2025999);
// 5. 测试判空函数
printf("判空结果:%d(1=空,0=非空)\n", is_empty_seqlist(class_list));
free(class_list);
class_list = NULL;
return 0;
}
二、关键函数逻辑详解
1. is_empty_seqlist 判空函数
- 核心逻辑 :顺序表的有效元素个数
n为0时,表为空。 - 边界处理 :
- 若传入的
l为NULL,返回-1表示参数错误; - 否则根据
l->n是否为0,返回1(空表)或0(非空表)。
- 若传入的
2. delete_data_seqlist 按id删除函数
顺序表删除元素的核心难点是填补删除后的"空缺" ,必须将目标位置之后的所有元素往前移动一位,否则会出现数据断层。
完整步骤:
- 安全检查:先判断顺序表是否存在、是否为空,避免空指针或空表操作;
- 查找目标:遍历数组,找到学号匹配的学生下标;
- 移动元素:将目标下标之后的元素依次往前覆盖,填补空缺;
- 更新数量 :有效元素个数
n减1。
三、运行效果示例
删除前:
===== 学生列表 =====
第1个学生:姓名:张三 | 学号:2025001 | 年龄:18
第2个学生:姓名:李四 | 学号:2025002 | 年龄:19
第3个学生:姓名:王五 | 学号:2025003 | 年龄:18
====================
✅ 成功删除学号为2025002的学生
删除后:
===== 学生列表 =====
第1个学生:姓名:张三 | 学号:2025001 | 年龄:18
第2个学生:姓名:王五 | 学号:2025003 | 年龄:18
====================
错误:未找到学号为2025999的学生
判空结果:0(1=空,0=非空)









linklist.h
c
#ifndef __LINKLIST_H__
#define __LINKLIST_H__
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef int datatype_t;
typedef struct node
{
datatype_t data; //数据域保存有效数据
struct node *next; //指针域保存下一个结点的地址
}linknode_t;
extern linknode_t *create_empty_linklist();
extern void insert_head_linklist(linknode_t *head,datatype_t data);
extern void printf_data_linklist(linknode_t *head);
extern void insert_tail_linklist(linknode_t *head,datatype_t data);
extern void insert_order_linklist(linknode_t *head,datatype_t data);
#endif
linklist.c
c
#include "linklist.h"
//1.创建空的链表---为头结点在堆区分配空间
linknode_t *create_empty_linklist()
{
linknode_t *head = NULL;
//1.1 分配堆区空间
head = (linknode_t *)malloc(sizeof(linknode_t));
if(NULL == head)
{
printf("malloc is fail!\n");
return NULL;
}
//memset(void *ptr, int value, size_t num):从内存地址 ptr 开始,把后续 num 字节的内存逐个字节设置为 value 的低 8 位。
//
memset(head,0,sizeof(linknode_t));
//head->next = NULL;
//head->data = 0;
return head;
}
//2.头插法:每次都在头结点后插入数据。
//特点:插入的顺序和输出的顺序是相反的。
void insert_head_linklist(linknode_t *head,datatype_t data)
{
//2.1 为结点在堆区申请空间
linknode_t *temp = (linknode_t *)malloc(sizeof(linknode_t));
if(NULL == temp)
{
printf("malloc is fail!\n");
return ;
}
//2.2 插入数据
temp->data = data;
//2.3 连接结点
temp->next = head->next;
head->next = temp;
return ;
}
//尾插法:每次都在尾结点后插入数据
void insert_tail_linklist(linknode_t *head,datatype_t data)
{
//2.1 为结点在堆区申请空间
linknode_t *temp = (linknode_t *)malloc(sizeof(linknode_t));
if(NULL == temp)
{
printf("malloc is fail!\n");
return ;
}
//2.2 插入数据
temp->data = data;
//3.找到尾结点
linknode_t *p = head;
while(p->next != NULL)
{
p = p->next;
}
//2.4 连接结点
temp->next = p->next;
p->next = temp;
return ;
}
//有序插入:每次都在尾结点后插入数据
void insert_order_linklist(linknode_t *head,datatype_t data)
{
//2.1 为结点在堆区申请空间
linknode_t *temp = (linknode_t *)malloc(sizeof(linknode_t));
if(NULL == temp)
{
printf("malloc is fail!\n");
return ;
}
//2.2 插入数据
temp->data = data;
linknode_t *p = head;
//2.3 p指针向后便利
while(p->next != NULL && data > p->next->data)
{
p = p->next;
}
//在p结点后插入temp结点
temp->next = p->next;
p->next = temp;
return ;
}
//3.输出链表种的内容
void printf_data_linklist(linknode_t *head)
{
linknode_t *p = head;
while(p->next != NULL)
{
printf("%d ",p->next->data);
p = p->next;
}
printf("\n");
return;
}
main.c
c
#include "linklist.h"
int main()
{
linknode_t *head = NULL;
datatype_t data;
int n = 0,i = 0;
head = create_empty_linklist();
printf("please input you want insert data number : ");
scanf("%d",&n);
printf("please input %d data : ",n);
for(i = 0;i <n;i++)
{
scanf("%d",&data);
//insert_head_linklist(head,data);
//insert_tail_linklist(head,data);
insert_order_linklist(head,data);
}
printf_data_linklist(head);
return 0;
}

题目解析与C语言实现
题目要求
- 构建单向链表
- 对数据
{1,5,3,7,9},采用有序插入方式,按**从大到小(降序)**存入链表 - 遍历输出链表
核心思路
- 单向链表节点:存储数据 + 指向下一节点的指针
- 有序插入逻辑(降序):
- 遍历链表,找到第一个比待插入值小的节点,将新节点插在它前面
- 若所有节点都比待插入值大,新节点插在链表尾部
- 若新节点比头节点还大,直接作为新头节点
- 依次插入 1、5、3、7、9,最终链表:
9 → 7 → 5 → 3 → 1
完整可运行代码
c
#include <stdio.h>
#include <stdlib.h>
// 1. 定义单向链表节点结构
typedef struct Node {
int data; // 存储数据
struct Node* next; // 指向下一个节点
} Node;
// 创建新节点
Node* create_node(int val) {
Node* new_node = (Node*)malloc(sizeof(Node));
if (new_node == NULL) {
perror("malloc failed");
exit(EXIT_FAILURE);
}
new_node->data = val;
new_node->next = NULL;
return new_node;
}
// 2. 有序插入:降序(从大到小)插入节点
void sorted_insert(Node** head, int val) {
Node* new_node = create_node(val);
// 情况1:空链表 或 新节点比头节点更大,作为新头
if (*head == NULL || new_node->data > (*head)->data) {
new_node->next = *head;
*head = new_node;
return;
}
// 情况2:遍历找到插入位置
Node* cur = *head;
// 找到第一个后继节点值 < 待插入值 的位置
while (cur->next != NULL && cur->next->data >= new_node->data) {
cur = cur->next;
}
// 插入到cur之后
new_node->next = cur->next;
cur->next = new_node;
}
// 3. 遍历输出链表
void print_list(Node* head) {
Node* p = head;
while (p != NULL) {
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
// 释放链表内存(避免内存泄漏)
void free_list(Node* head) {
Node* tmp;
while (head != NULL) {
tmp = head;
head = head->next;
free(tmp);
}
}
int main() {
Node* head = NULL; // 初始化空链表
int arr[] = {1, 5, 3, 7, 9};
int n = sizeof(arr)/sizeof(arr[0]);
// 逐个有序插入
for (int i = 0; i < n; i++) {
sorted_insert(&head, arr[i]);
}
printf("降序单向链表结果:");
print_list(head);
free_list(head);
return 0;
}
运行结果
降序单向链表结果:9 7 5 3 1
插入过程分步演示(帮你理解有序插入)
- 插入
1:链表[1] - 插入
5:5>1 → 头插 →[5, 1] - 插入
3:5>3>1 → 插在5和1之间 →[5, 3, 1] - 插入
7:7>5 → 头插 →[7, 5, 3, 1] - 插入
9:9>7 → 头插 →[9, 7, 5, 3, 1]


linklist.h
c
#ifndef __LINKLIST_H__
#define __LINKLIST_H__
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef int datatype_t;
typedef struct node
{
datatype_t data; //数据域保存有效数据
struct node *next; //指针域保存下一个结点的地址
}linknode_t;
extern linknode_t *create_empty_linklist();
extern void insert_head_linklist(linknode_t *head,datatype_t data);
extern void printf_data_linklist(linknode_t *head);
extern void insert_tail_linklist(linknode_t *head,datatype_t data);
extern void insert_order_linklist(linknode_t *head,datatype_t data);
extern int is_empty_linklist(linknode_t *head);
extern int delete_data_linklist(linknode_t *head,datatype_t data);
#endif
linklist.c
c
#include "linklist.h"
//1.创建空的链表---为头结点在堆区分配空间
linknode_t *create_empty_linklist()
{
linknode_t *head = NULL;
//1.1 分配堆区空间
head = (linknode_t *)malloc(sizeof(linknode_t));
if(NULL == head)
{
printf("malloc is fail!\n");
return NULL;
}
memset(head,0,sizeof(linknode_t));
//head->next = NULL;
//head->data = 0;
return head;
}
//2.头插法:每次都在头结点后插入数据。
//特点:插入的顺序和输出的顺序是相反的。
void insert_head_linklist(linknode_t *head,datatype_t data)
{
//2.1 为结点在堆区申请空间
linknode_t *temp = (linknode_t *)malloc(sizeof(linknode_t));
if(NULL == temp)
{
printf("malloc is fail!\n");
return ;
}
//2.2 插入数据
temp->data = data;
//2.3 连接结点
temp->next = head->next;
head->next = temp;
return ;
}
//尾插法:每次都在尾结点后插入数据
void insert_tail_linklist(linknode_t *head,datatype_t data)
{
//2.1 为结点在堆区申请空间
linknode_t *temp = (linknode_t *)malloc(sizeof(linknode_t));
if(NULL == temp)
{
printf("malloc is fail!\n");
return ;
}
//2.2 插入数据
temp->data = data;
//3.找到尾结点
linknode_t *p = head;
while(p->next != NULL)
{
p = p->next;
}
//2.4 连接结点
temp->next = p->next;
p->next = temp;
return ;
}
//有序插入:每次都在尾结点后插入数据
void insert_order_linklist(linknode_t *head,datatype_t data)
{
//2.1 为结点在堆区申请空间
linknode_t *temp = (linknode_t *)malloc(sizeof(linknode_t));
if(NULL == temp)
{
printf("malloc is fail!\n");
return ;
}
//2.2 插入数据
temp->data = data;
linknode_t *p = head;
//2.3 p指针向后便利
while(p->next != NULL && data > p->next->data)
{
p = p->next;
}
//在p结点后插入temp结点
temp->next = p->next;
p->next = temp;
return ;
}
//3.输出链表种的内容
void printf_data_linklist(linknode_t *head)
{
linknode_t *p = head;
while(p->next != NULL)
{
printf("%d ",p->next->data);
p = p->next;
}
printf("\n");
return;
}
int is_empty_linklist(linknode_t *head)
{
return head->next == NULL ? 1 : 0;
}
int delete_data_linklist(linknode_t *head,datatype_t data)
{
linknode_t *p = NULL;
linknode_t *q = NULL;
int flag = 0;
if(is_empty_linklist(head))
{
return -1;
}
p = head;
//没有遍历到链表尾部
while(p->next != NULL)
{
if(p->next->data == data)
{
//保存要删除结点的地址
q = p->next;
p->next = q->next;
free(q);
q = NULL;
flag = 1;
}else{
p = p->next;
}
}
if(flag == 0)
{
return -2;
}else{
printf("delete %d is successful!\n",data);
return 0;
}
}
main.c
c
#include "linklist.h"
int main()
{
linknode_t *head = NULL;
datatype_t data;
int n = 0,i = 0;
int ret = 0;
head = create_empty_linklist();
printf("please input you want insert data number : ");
scanf("%d",&n);
printf("please input %d data : ",n);
for(i = 0;i <n;i++)
{
scanf("%d",&data);
//insert_head_linklist(head,data);
//insert_tail_linklist(head,data);
insert_order_linklist(head,data);
}
printf_data_linklist(head);
printf("please input you want delete data : ");
scanf("%d",&data);
ret = delete_data_linklist(head,data);
if(ret < 0)
{
printf("data is not exist or is empty!\n");
return -1;
}
printf_data_linklist(head);
return 0;
}

题目要求
- 构建单向链表,将数据
{1,5,3,7,9,5,8,5,3}有序降序插入链表 - 输出插入完成后的链表
- 删除链表中所有值为3的节点
- 再次输出处理后的链表
C语言完整实现
c
#include <stdio.h>
#include <stdlib.h>
// 定义单向链表节点
typedef struct Node {
int data;
struct Node* next;
} Node;
// 创建新节点
Node* create_node(int val) {
Node* new_node = (Node*)malloc(sizeof(Node));
if (new_node == NULL) {
perror("malloc failed");
exit(EXIT_FAILURE);
}
new_node->data = val;
new_node->next = NULL;
return new_node;
}
// 有序插入:从大到小降序
void sorted_insert(Node** head, int val) {
Node* new_node = create_node(val);
// 空链表 / 新节点比头节点大,头插
if (*head == NULL || new_node->data > (*head)->data) {
new_node->next = *head;
*head = new_node;
return;
}
// 遍历找插入位置
Node* cur = *head;
while (cur->next != NULL && cur->next->data >= new_node->data) {
cur = cur->next;
}
new_node->next = cur->next;
cur->next = new_node;
}
// 删除所有值为3的节点
void remove_all_val(Node** head, int target) {
// 处理头节点连续为target的情况
while (*head != NULL && (*head)->data == target) {
Node* tmp = *head;
*head = (*head)->next;
free(tmp);
}
if (*head == NULL) return;
Node* cur = *head;
while (cur->next != NULL) {
if (cur->next->data == target) {
Node* tmp = cur->next;
cur->next = cur->next->next;
free(tmp);
} else {
cur = cur->next;
}
}
}
// 遍历输出链表
void print_list(Node* head) {
Node* p = head;
while (p != NULL) {
printf("%d ", p->data);
p = p->next;
}
printf("\n");
}
// 释放链表内存
void free_list(Node* head) {
Node* tmp;
while (head != NULL) {
tmp = head;
head = head->next;
free(tmp);
}
}
int main() {
Node* head = NULL;
int arr[] = {1,5,3,7,9,5,8,5,3};
int n = sizeof(arr)/sizeof(arr[0]);
// 有序降序插入
for (int i = 0; i < n; i++) {
sorted_insert(&head, arr[i]);
}
printf("插入后链表:");
print_list(head);
// 删除所有值为3的节点
remove_all_val(&head, 3);
printf("删除所有3后:");
print_list(head);
free_list(head);
return 0;
}
运行结果
插入后链表:9 8 7 5 5 5 3 3 1
删除所有3后:9 8 7 5 5 5 1
关键逻辑说明
-
有序降序插入
- 若新节点比头节点大,直接作为新头
- 否则遍历链表,找到第一个后继节点小于当前值的位置,插入到该节点之后
-
删除所有目标值(3)
- 先循环处理头部连续为3的节点,更新头指针
- 再遍历剩余链表,匹配到值为3的节点,断开链接并释放内存,避免内存泄漏



linklist.h
c
#ifndef __LINKLIST_H__
#define __LINKLIST_H__
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef int datatype_t;
typedef struct node
{
datatype_t data; //数据域保存有效数据
struct node *next; //指针域保存下一个结点的地址
}linknode_t;
extern linknode_t *create_empty_linklist();
extern void insert_head_linklist(linknode_t *head,datatype_t data);
extern void printf_data_linklist(linknode_t *head);
extern void insert_tail_linklist(linknode_t *head,datatype_t data);
extern void insert_order_linklist(linknode_t *head,datatype_t data);
extern int is_empty_linklist(linknode_t *head);
extern int delete_data_linklist(linknode_t *head,datatype_t data);
extern void reverse_data_linklist(linknode_t *head);
extern void clean_up_linklist(linknode_t *head);
#endif
linklist.c
c
#include "linklist.h"
//1.创建空的链表---为头结点在堆区分配空间
linknode_t *create_empty_linklist()
{
linknode_t *head = NULL;
//1.1 分配堆区空间
head = (linknode_t *)malloc(sizeof(linknode_t));
if(NULL == head)
{
printf("malloc is fail!\n");
return NULL;
}
memset(head,0,sizeof(linknode_t));
//head->next = NULL;
//head->data = 0;
return head;
}
//2.头插法:每次都在头结点后插入数据。
//特点:插入的顺序和输出的顺序是相反的。
void insert_head_linklist(linknode_t *head,datatype_t data)
{
//2.1 为结点在堆区申请空间
linknode_t *temp = (linknode_t *)malloc(sizeof(linknode_t));
if(NULL == temp)
{
printf("malloc is fail!\n");
return ;
}
//2.2 插入数据
temp->data = data;
//2.3 连接结点
temp->next = head->next;
head->next = temp;
return ;
}
//尾插法:每次都在尾结点后插入数据
void insert_tail_linklist(linknode_t *head,datatype_t data)
{
//2.1 为结点在堆区申请空间
linknode_t *temp = (linknode_t *)malloc(sizeof(linknode_t));
if(NULL == temp)
{
printf("malloc is fail!\n");
return ;
}
//2.2 插入数据
temp->data = data;
//3.找到尾结点
linknode_t *p = head;
while(p->next != NULL)
{
p = p->next;
}
//2.4 连接结点
temp->next = p->next;
p->next = temp;
return ;
}
//有序插入:每次都在尾结点后插入数据
void insert_order_linklist(linknode_t *head,datatype_t data)
{
//2.1 为结点在堆区申请空间
linknode_t *temp = (linknode_t *)malloc(sizeof(linknode_t));
if(NULL == temp)
{
printf("malloc is fail!\n");
return ;
}
//2.2 插入数据
temp->data = data;
linknode_t *p = head;
//2.3 p指针向后便利
while(p->next != NULL && data > p->next->data)
{
p = p->next;
}
//在p结点后插入temp结点
temp->next = p->next;
p->next = temp;
return ;
}
//3.输出链表种的内容
void printf_data_linklist(linknode_t *head)
{
linknode_t *p = head;
while(p->next != NULL)
{
printf("%d ",p->next->data);
p = p->next;
}
printf("\n");
return;
}
int is_empty_linklist(linknode_t *head)
{
return head->next == NULL ? 1 : 0;
}
int delete_data_linklist(linknode_t *head,datatype_t data)
{
linknode_t *p = NULL;
linknode_t *q = NULL;
int flag = 0;
if(is_empty_linklist(head))
{
return -1;
}
p = head;
//没有遍历到链表尾部
while(p->next != NULL)
{
if(p->next->data == data)
{
//保存要删除结点的地址
q = p->next;
p->next = q->next;
free(q);
q = NULL;
flag = 1;
}else{
p = p->next;
}
}
if(flag == 0)
{
return -2;
}else{
printf("delete %d is successful!\n",data);
return 0;
}
}
//链表的逆序
void reverse_data_linklist(linknode_t *head)
{
linknode_t *p = NULL;
linknode_t *q = NULL;
//1.保存第二个有效结点的地址,再把第一个有效结点的指针域设置为NULL
p = head->next->next;
head->next->next = NULL;
//2.从p结点后,利用头插法的思想在头结点后插入数据。
// q保存p后一个结点的地址
while(p != NULL)
{
q = p->next;
p->next = head->next;
head->next = p;
p = q;
}
return ;
}
//删除链表中所有的结点
void clean_up_linklist(linknode_t *head)
{
linknode_t *p = head;
linknode_t *q = NULL;
while(p != NULL)
{
q = p->next;
printf_data_linklist(p);
free(p);
p = q;
}
return ;
}
main.c
c
#include "linklist.h"
int main()
{
linknode_t *head = NULL;
datatype_t data;
int n = 0,i = 0;
int ret = 0;
head = create_empty_linklist();
printf("please input you want insert data number : ");
scanf("%d",&n);
printf("please input %d data : ",n);
for(i = 0;i <n;i++)
{
scanf("%d",&data);
//insert_head_linklist(head,data);
//insert_tail_linklist(head,data);
insert_order_linklist(head,data);
}
printf_data_linklist(head);
#if 0/*{{{*/
printf("please input you want delete data : ");
scanf("%d",&data);
ret = delete_data_linklist(head,data);
if(ret < 0)
{
printf("data is not exist or is empty!\n");
return -1;
}
printf_data_linklist(head);
#endif/*}}}*/
//reverse_data_linklist(head);
//printf_data_linklist(head);
printf("=========================\n");
clean_up_linklist(head);
return 0;
}


looplist.h
c
#ifndef __LOOPLIST_H__
#define __LOOPLIST_H__
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef int datatype_t;
typedef struct node
{
datatype_t data; //数据域保存有效数据
struct node *next; //指针域保存下一个结点的地址
}loopnode_t;
extern loopnode_t *create_empty_looplist();
extern void insert_head_looplist(loopnode_t *head,datatype_t data);
extern void printf_data_looplist(loopnode_t *head);
extern int is_empty_looplist(loopnode_t *head);
#endif
looplist.c
c
#include "looplist.h"
//1.创建空的链表---为头结点在堆区分配空间
loopnode_t *create_empty_looplist()
{
loopnode_t *head = NULL;
//1.1 分配堆区空间
head = (loopnode_t *)malloc(sizeof(loopnode_t));
if(NULL == head)
{
printf("malloc is fail!\n");
return NULL;
}
memset(head,0,sizeof(loopnode_t));
head->next = head;
return head;
}
//2.头插法:每次都在头结点后插入数据。
//特点:插入的顺序和输出的顺序是相反的。
void insert_head_looplist(loopnode_t *head,datatype_t data)
{
//2.1 为结点在堆区申请空间
loopnode_t *temp = (loopnode_t *)malloc(sizeof(loopnode_t));
if(NULL == temp)
{
printf("malloc is fail!\n");
return ;
}
//2.2 插入数据
temp->data = data;
//2.3 连接结点
temp->next = head->next;
head->next = temp;
return ;
}
//3.输出链表种的内容
void printf_data_looplist(loopnode_t *head)
{
loopnode_t *p = head;
while(p->next != head)
{
printf("%d ",p->next->data);
p = p->next;
}
printf("\n");
return;
}
int is_empty_looplist(loopnode_t *head)
{
return head->next == NULL ? 1 : 0;
}
main.c
c
#include "looplist.h"
int main()
{
loopnode_t *head = NULL;
datatype_t data;
int n = 0,i = 0;
int ret = 0;
head = create_empty_looplist();
printf("please input you want insert data number : ");
scanf("%d",&n);
printf("please input %d data : ",n);
for(i = 0;i <n;i++)
{
scanf("%d",&data);
insert_head_looplist(head,data);
}
printf_data_looplist(head);
return 0;
}











seqstack.h
c
#ifndef __SEQSTACK_H__
#define __SEQSTACK_H__
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef int data_t;
#define MAX 5
typedef struct
{
data_t buf[MAX];//数组存储数据
int top; //记录栈顶元素的下标
}seqstack_t;
extern seqstack_t *create_empty_seqstack();
extern void push_seqstack(seqstack_t *s,data_t data);
extern data_t pop_seqstack(seqstack_t *s);
extern int is_empty_seqstack(seqstack_t *s);
extern int is_full_seqstack(seqstack_t *s);
extern int get_top_seqstack(seqstack_t *s);
#endif
seqstack.c
c
#include "seqstack.h"
//1.创建空的顺序栈------为结构体在堆区分配空间
seqstack_t *create_empty_seqstack()
{
//1.1 堆区分配空间
seqstack_t *s = NULL;
s = (seqstack_t *)malloc(sizeof(seqstack_t));
if(NULL == s)
{
printf("malloc is fail!\n");
return NULL;
}
memset(s,0,sizeof(seqstack_t));
s->top = -1;
return s;
}
//2.入栈,先移动top,在存放数据
void push_seqstack(seqstack_t *s,data_t data)
{
s->buf[++s->top] = data;
return ;
}
//3.出栈,先取出数据,移动top
data_t pop_seqstack(seqstack_t *s)
{
return s->buf[s->top--];
}
//4.判断空栈
int is_empty_seqstack(seqstack_t *s)
{
return s->top == -1 ? 1 : 0;
}
//5.判断满栈
int is_full_seqstack(seqstack_t *s)
{
return s->top == MAX - 1 ? 1 : 0;
}
//6.获得栈顶元素的值------------返回数组最后一个入栈元素的值
int get_top_seqstack(seqstack_t *s)
{
return s->buf[s->top];
}
main.c
c
#include "seqstack.h"
int main()
{
int i = 0,ret = 0;
seqstack_t *s = NULL;
s = create_empty_seqstack();
//当栈没有满的时候,插入数据
while(!is_full_seqstack(s))
{
push_seqstack(s,i++);
}
//0 1 2 3 4 top pop
printf("top : %d\n",get_top_seqstack(s));
printf("pop : %d\n",pop_seqstack(s));
//栈不为空的时候,出栈数据
while(!is_empty_seqstack(s))
{
ret = pop_seqstack(s);
printf("%d ",ret);
}
printf("\n");
return 0;
}



linkstack.h
c
#ifndef __LINKSTACK_H__
#define __LINKSTACK_H__
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
//结点类型的设计
typedef char data_t;
typedef struct node
{
data_t data;
struct node *next;
}linknode_t;
//栈头类型的设计
typedef struct
{
linknode_t *top;//栈顶指针
int n; //记录当前栈种元素的个数
}linkstack_t;
extern linkstack_t *create_empty_linkstack();
extern int is_empty_linkstack(linkstack_t *s);
extern void push_linkstack(linkstack_t *s,data_t data);
extern data_t pop_linkstack(linkstack_t *s);
extern data_t get_top_data(linkstack_t *s);
#endif
linkstack.c
c
#include "linkstack.h"
//1.创建空的链式栈------为栈头在堆区分配空间
linkstack_t *create_empty_linkstack()
{
linkstack_t *s = NULL;
s = (linkstack_t *)malloc(sizeof(linkstack_t));
if(NULL == s)
{
printf("malloc is fail!\n");
return NULL;
}
memset(s,0,sizeof(linkstack_t));
return s;
}
//2.判断栈是否为空
int is_empty_linkstack(linkstack_t *s)
{
return s->top == NULL ? 1 : 0;
}
//3.入栈
void push_linkstack(linkstack_t *s,data_t data)
{
linknode_t *temp = NULL;
temp = (linknode_t *)malloc(sizeof(linknode_t));
if(NULL == temp)
{
printf("malloc is fail!\n");
return ;
}
temp->data = data;
//插入数据,类似于链表的头插法
temp->next = s->top;
s->top = temp;
//更新n的值
s->n ++;
return ;
}
//4.出栈
data_t pop_linkstack(linkstack_t *s)
{
linknode_t *temp = NULL;
data_t data;
//1.保存要删除结点的地址
temp = s->top;
//2.取出数据
data = temp->data;
//3.更新指针信息
s->top = temp->next;
//4.释放temp结点
free(temp);
temp = NULL;
//5.更新n的值
s->n --;
return data;
}
//5.输出栈顶元素的值
data_t get_top_data(linkstack_t *s)
{
return s->top->data;
}
main.c
c
#include "linkstack.h"
int main()
{
linkstack_t *s = NULL;
data_t array[] = {'a','n','i','h','c'};
int i = 0;
s = create_empty_linkstack();
for(i = 0;i <sizeof(array)/sizeof(array[0]);i++)
{
push_linkstack(s,array[i]);
}
printf("Top data = %c\n",get_top_data(s));
while(!is_empty_linkstack(s))
{
printf("%c",pop_linkstack(s));
}
printf("\n");
return 0;
}





loopqueue.h
c
#ifndef __LOOPQUEUE_H__
#define __LOOPQUEUE_H__
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define N 5
typedef int data_t;
typedef struct
{
data_t buf[N]; //定义数组存储数据
int front; //队头元素的下标
int rear; //队尾元素下一个元素的下标
}loopqueue_t;
extern loopqueue_t *create_empty_loopqueue();
extern int is_empty_loopqueue(loopqueue_t *q);
extern int is_full_loopqueue(loopqueue_t *q);
extern void enter_loopqueue(loopqueue_t *q,data_t data);
extern data_t delete_loopqueue(loopqueue_t *q);
#endif
loopqueue.c
c
#include "loopqueue.h"
//1.创建空的循环队列
loopqueue_t *create_empty_loopqueue()
{
loopqueue_t *q = NULL;
q = (loopqueue_t *)malloc(sizeof(loopqueue_t));
if(NULL == q)
{
printf("malloc is fail!\n");
return NULL;
}
memset(q,0,sizeof(loopqueue_t));
q->front = q->rear = 0;
return q;
}
//2.队空
int is_empty_loopqueue(loopqueue_t *q)
{
return q->front == q->rear ? 1 : 0;
}
//3.队满
int is_full_loopqueue(loopqueue_t *q)
{
return (q->front == (q->rear + 1) % N) ? 1 : 0;
}
//4.入队
void enter_loopqueue(loopqueue_t *q,data_t data)
{
q->buf[q->rear] = data;
q->rear = (q->rear + 1) % N;
return ;
}
//5.出队
data_t delete_loopqueue(loopqueue_t *q)
{
data_t data;
data = q->buf[q->front];
q->front = (q->front + 1) % N;
return data;
}
main.c
c
#include "loopqueue.h"
int main(int argc, const char *argv[])
{
int i = 0;
int j = 2;
loopqueue_t *q = NULL;
q = create_empty_loopqueue();
while(!is_full_loopqueue(q))
{
enter_loopqueue(q,i++);
}
while(!is_empty_loopqueue(q))
{
printf("%d ",delete_loopqueue(q));
}
printf("\n");
return 0;
}


linkstack.h
c
#ifndef __LINKSTACH_H__
#define __LINKSTACH_H__
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
//链表结点的设计
typedef int data_t;
typedef struct node
{
data_t data;
struct node *next;
}linknode_t;
//队列头的设计
typedef struct
{
linknode_t *front;
linknode_t *rear;
}linkqueue_t;
//1.创建一个空的链式队列.为队列头和头结点分配空间
extern linkqueue_t *create_empty_linkqueue();
//2.判空
extern int is_empty_linkqueue(linkqueue_t *q);
//3.入队
void enter_linkqueue(linkqueue_t *q,data_t data);
//4.出队
data_t delete_linkqueue(linkqueue_t *q);
#endif
linkstack.c
c
#include "linkstack.h"
//1.创建一个空的链式队列.为队列头和头结点分配空间
linkqueue_t *create_empty_linkqueue()
{
linkqueue_t *q = NULL;
linknode_t *head = NULL;
//1.先给头结点分配空间
head = (linknode_t *)malloc(sizeof(linknode_t));
head->next = NULL;
//2.为队列头分配空间
q = (linkqueue_t *)malloc(sizeof(linkqueue_t));
q->front = q->rear = head;
//返回队列头
return q;
}
//2.判空
int is_empty_linkqueue(linkqueue_t *q)
{
return q->front == q->rear ? 1 : 0;
}
//3.入队
void enter_linkqueue(linkqueue_t *q,data_t data)
{
linknode_t *temp = NULL;
temp = (linknode_t *)malloc(sizeof(linknode_t));
temp->data = data;
temp->next = q->rear->next;
q->rear->next = temp;
//更新尾指针
q->rear = temp;
return ;
}
//4.出队
data_t delete_linkqueue(linkqueue_t *q)
{
linknode_t *temp = NULL;
data_t data;
//1.保存要删除结点的地址,取出数据
temp = q->front->next;
data = temp->data;
//2.释放结点
q->front->next = temp->next;
free(temp);
temp = NULL;
//3.若是为空,q->front->next == NULL.
if(q->front->next == NULL)
{
q->rear = q->front;
}
return data;
}
main.c
c
#include "linkstack.h"
int main()
{
linkqueue_t *q = NULL;
int i = 0;
q = create_empty_linkqueue();
for(i = 0;i < 10;i++)
{
enter_linkqueue(q,i);
}
while(!is_empty_linkqueue(q))
{
printf("%d ",delete_linkqueue(q));
}
printf("\n");
return 0;
}