1. 数据结构简单复习回顾(线性结构)

我们来一步步拆解这个练习,从理解结构到写出获取第一个学生 id 的完整代码,同时讲清边界处理和注意事项。


一、先理清代码结构

你提供的代码定义了两个核心类型:

  1. struct student(学生类型) :存储单个学生的 name(姓名)、id(学号)、age(年龄),并通过 typedef 重命名为 datatype_t,方便后续顺序表使用。
  2. 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] 位置
  • 学生的 idstruct 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

  • stseqlist_t 类型变量,st.buf 是学生数组;
  • 数组下标从 0 开始,st.buf[0] 代表数组的第一个元素(第一个学生);
  • .idstruct 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 的核心步骤:

  1. 定义 seqlist_t st
  2. 初始化并添加学生,确保 st.n >= 1
  3. 通过 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
===========================

四、补充说明

  1. 尾插逻辑 :这里默认实现的是尾部插入 (新学生加在列表最后),如果需要指定位置插入,需要额外写一个insert_pos_seqlist函数。
  2. 边界保护 :所有函数都做了l == NULL的判断,防止空指针访问导致程序崩溃。
  3. 内存管理 :主函数最后用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 判空函数

  • 核心逻辑 :顺序表的有效元素个数 n0 时,表为空。
  • 边界处理
    • 若传入的 lNULL,返回 -1 表示参数错误;
    • 否则根据 l->n 是否为 0,返回 1(空表)或 0(非空表)。

2. delete_data_seqlist 按id删除函数

顺序表删除元素的核心难点是填补删除后的"空缺" ,必须将目标位置之后的所有元素往前移动一位,否则会出现数据断层。

完整步骤:

  1. 安全检查:先判断顺序表是否存在、是否为空,避免空指针或空表操作;
  2. 查找目标:遍历数组,找到学号匹配的学生下标;
  3. 移动元素:将目标下标之后的元素依次往前覆盖,填补空缺;
  4. 更新数量 :有效元素个数 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. 构建单向链表
  2. 对数据 {1,5,3,7,9},采用有序插入方式,按**从大到小(降序)**存入链表
  3. 遍历输出链表

核心思路

  1. 单向链表节点:存储数据 + 指向下一节点的指针
  2. 有序插入逻辑(降序):
    • 遍历链表,找到第一个比待插入值小的节点,将新节点插在它前面
    • 若所有节点都比待插入值大,新节点插在链表尾部
    • 若新节点比头节点还大,直接作为新头节点
  3. 依次插入 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:链表 [1]
  2. 插入 5:5>1 → 头插 → [5, 1]
  3. 插入 3:5>3>1 → 插在5和1之间 → [5, 3, 1]
  4. 插入 7:7>5 → 头插 → [7, 5, 3, 1]
  5. 插入 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. 构建单向链表,将数据 {1,5,3,7,9,5,8,5,3} 有序降序插入链表
  2. 输出插入完成后的链表
  3. 删除链表中所有值为3的节点
  4. 再次输出处理后的链表

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 

关键逻辑说明

  1. 有序降序插入

    • 若新节点比头节点大,直接作为新头
    • 否则遍历链表,找到第一个后继节点小于当前值的位置,插入到该节点之后
  2. 删除所有目标值(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;
}
相关推荐
Irissgwe1 小时前
二叉树进阶
数据结构·c++·算法·c·二叉搜索树
hairenwangmiao1 小时前
c++排序(第一章----桶排序与sort排序)
数据结构·c++·排序
xieliyu.1 小时前
Java数据结构:从0开始手搓Hash桶
java·数据结构·哈希算法
Irissgwe2 小时前
数据结构-二叉树
数据结构·c++·二叉树·c·
山峰哥2 小时前
VBA数据结构之争:Dictionary vs Collection,性能差3倍!
服务器·数据结构·数据库·windows·sql·算法·哈希算法
青山木12 小时前
Hot 100 --- 轮转数组
java·数据结构·算法
WBluuue15 小时前
数据结构与算法:有序表(二):跳表
数据结构·c++·算法·skiplist
不好听61316 小时前
深入理解链表:线性数据结构的另一面
javascript·数据结构
Queenie_Charlie17 小时前
哈夫曼树
数据结构·c++·哈夫曼树