数据结构基础练习 | 单链表操作全套实战与复盘


📖 推荐阅读:《Yocto项目实战教程:高效定制嵌入式Linux系统
🎥 更多学习视频请关注 B 站:嵌入式Jerry



数据结构基础练习 | 单链表操作全套实战与复盘

前言

单链表是数据结构与算法学习的核心内容之一,不仅是算法题的高频考点,更是C语言面试和实际工程开发的基础能力。通过扎实掌握单链表的各种基本操作,可以极大提升你的编程思维与指针理解能力。本练习代码涵盖了链表创建、打印、反转、插入(头、尾、任意位置)、删除(指定位置)等典型功能,每一个操作都对应着真实项目和面试中的核心用法。


一、链表的核心用途和实战意义

  1. 高效插入/删除:链表结构在已知节点指针的情况下,插入和删除都能做到O(1)时间复杂度,优于数组的O(n)。
  2. 动态内存管理:链表在插入、删除节点时无需移动大量元素,内存利用率高,适合实现队列、栈、缓存等结构。
  3. 算法面试基础:链表题目能全面考查指针运算、内存管理、边界处理,是算法笔试/面试绕不开的内容。
  4. 工程开发常见场景:如操作系统进程链表、LRU缓存、哈希拉链法、邻接表等,都需要链表的熟练应用。

在这里插入图片描述

二、练习意图与目标

  • 基础夯实:通过自写链表各类基础操作,打牢指针和内存操作的基本功。
  • 边界覆盖:全面训练空链表、单节点、头/尾插入、越界等特殊场景,强化代码健壮性。
  • 代码复用与归纳:将常用链表操作形成模板,积累个人算法/工程工具库。
  • 实战思维提升:每个操作不仅为算法题做准备,更贴合实际开发需求,提升综合能力。

三、完整代码与注释

#include <stdio.h>
#include <stdlib.h>

// 单链表节点定义
typedef struct ListNode {
    int val;
    struct ListNode* next;
} ListNode;

// 创建链表(数组转链表)
ListNode* createList(int* arr, int len) {
    if(len <= 0)
        return NULL;
    int i = 0;
    ListNode * head = malloc(sizeof(ListNode));
    if (!head) return NULL;
    head->val = arr[i++];
    ListNode * tmp = head;
    while(--len){
        ListNode * nt = malloc(sizeof(ListNode));
        if (!nt) return head; // 分配失败提前返回
        nt->val = arr[i++];
        tmp->next = nt;
        tmp = nt;
    }
    tmp->next = NULL;
    return head;
}

// 打印链表
void printList(ListNode* head) {
    ListNode* p = head;
    while (p) {
        printf("%d", p->val);
        if (p->next) printf(" -> ");
        p = p->next;
    }
    printf("\n");
}

// 释放链表
void freeList(ListNode* head) {
    while (head) {
        ListNode* tmp = head;
        head = head->next;
        free(tmp);
    }
}

// 链表反转
ListNode* reverseList(ListNode* head) {
    ListNode * pre = NULL;
    while(head){
        ListNode * nt = head->next;
        head->next = pre;
        pre = head;
        head = nt;
    }
    return pre;
}

// 头插法
ListNode* insertHead(ListNode* head, int val) {
    ListNode* node = malloc(sizeof(ListNode));
    node->val = val;
    node->next = head;
    return node;
}

// 尾插法
ListNode* insertTail(ListNode* head, int val) {
    ListNode* node = malloc(sizeof(ListNode));
    node->val = val;
    node->next = NULL;
    if(head==NULL)
        return node;
    ListNode* tmp = head;
    while(tmp->next)
        tmp = tmp->next;
    tmp->next = node;
    return head;
}

// 指定位置插入
ListNode* insertAt(ListNode* head, int pos, int val) {
    ListNode * node = malloc(sizeof(ListNode));
    node->val = val;
    node->next = NULL;
    if(pos == 0){
        node->next = head;
        return node;
    }
    ListNode* pre = head;
    ListNode* nt = head->next;
    int i = 1;
    while(i < pos && nt){
        pre = pre->next;
        nt = nt->next;
        i++;
    }
    pre->next = node;
    node->next = nt;
    return head;
}

// 指定位置删除
ListNode* deleteAt(ListNode* head, int pos) {
    if(pos == 0 && head){
        ListNode* nt = head->next;
        free(head);
        return nt;
    }
    ListNode* pre = head;
    ListNode* nt = head ? head->next : NULL;
    int i = 1;
    while(i < pos && nt){
        pre = pre->next;
        nt = nt->next;
        i++;
    }
    if(nt){
        pre->next = nt->next;
        free(nt);
    }
    return head;
}

// main函数测试
int main() {
    int arr[] = {1,2,3,4,5};
    ListNode* head = createList(arr, 5);
    printf("原链表: ");
    printList(head);

    // 头插
    head = insertHead(head, 11);
    printf("头插11: ");
    printList(head);

    // 尾插
    head = insertTail(head, 12);
    printf("尾插12: ");
    printList(head);

    // 指定位置插入
    head = insertAt(head, 5, 13);
    printf("在第5个位置插入13: ");
    printList(head);

    // 删除第4个节点
    head = deleteAt(head, 4);
    printf("删除第4个节点: ");
    printList(head);

    // 反转链表
    head = reverseList(head);
    printf("反转后链表: ");
    printList(head);

    freeList(head);
    return 0;
}

四、各操作的核心用途与练习意图

1. 链表创建与销毁

  • 用途:把数据结构与C的内存管理相结合,理解链表“动态生成与释放”的全过程。
  • 意图:培养“用完即释放”的习惯,防止内存泄漏,理解malloc/free的本质。

2. 打印链表

  • 用途:调试和可视化链表内容,验证操作正确性。
  • 意图:锻炼“调试-输出-验证”能力,为复杂链表题目做准备。

3. 头插/尾插/指定位置插入

  • 用途:快速在链表任何位置插入节点,实现队列、栈、哈希表拉链等实际结构。
  • 意图:熟悉指针操作,学会处理各种边界(如头/尾/越界/空链表等)。

4. 指定位置删除

  • 用途:灵活删除链表任意节点,贴合实际工程与算法需求(如LRU淘汰、队列出队等)。
  • 意图:练习节点定位、指针连接与内存释放,理解链表动态修改的全过程。

5. 链表反转

  • 用途:算法面试高频考点,实际工程中如链表逆序打印、撤销操作、历史栈管理等。
  • 意图:掌握三指针法,体会指针移动的精髓,构建更复杂的链表操作模板。

五、实战应用举例

  • 队列/栈底层实现:队列常用尾插头删,栈常用头插头删,链表轻松支持。
  • 哈希表拉链法:每个桶挂一条链表,插入删除查找都用链表实现。
  • 操作系统进程/任务管理:任务链表、定时器链表等动态插入/删除频繁。
  • 缓存与调度:如LRU缓存、消息队列、任务池等。

六、学习与复盘建议

  1. 多写多练,手写最扎实:每个函数都动手写一遍,绝不抄现成代码。
  2. 注重边界和异常处理:空链表、单节点、越界、分配失败都要测一测。
  3. 练习模板归纳:总结每种操作的固定模板,形成个人“代码片段库”。
  4. 与实际项目对照:思考这些基本操作在真实工程(如队列、缓存)中的应用。
  5. 持续优化和补充:尝试加上链表查找、环检测、合并、排序等进阶功能。

七、结语

单链表操作的熟练掌握,是通往高级数据结构、算法和工程开发的基础门槛。
建议将本套练习反复复习、举一反三,结合实际项目和LeetCode题目进行巩固,逐步形成自己的数据结构体系。
遇到问题及时查阅/请教,写完每一题都要理解透彻,只有这样,才能在面试和实战中从容应对各种链表问题!


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值