首页 > 趣闻 > 正文内容

C语言中如何实现链表增删改查?代码示例+步骤解析

趣闻2025-05-27 16:47:55

??"哎哟喂!每次看到链表就手抖?增删改查四件套搞得你怀疑人生?"??
别慌,今天咱们就撸起袖子,用最接地气的方式把这四个操作拆解明白。我敢保证,看完这篇你至少能少掉三根头发(别问我怎么知道的)!


一、增删改查到底难在哪?

(扶额)先泼盆冷水清醒下:??链表操作的核心其实就是玩指针??。就像小时候玩翻花绳,绳子(指针)绕错了整个结构就垮了。不过别怕,咱们先理清三个关键点:

  1. ??节点结构??:数据仓库+下一站车票
c复制
struct Node {
    int data;          // 你的数据(比如年龄、分数)
    struct Node* next; // 开往下一节车厢的火车票
};
  1. ??头指针??:永远捏着火车头的乘务长
  2. ??内存管理??:malloc和free就像借书卡,用完得归还

二、增:把新乘客塞进火车

(搓手)咱们用实际场景说话——现在要给学生成绩单链表加个新学生:

??场景1:插队到最前面(头插法)??

c复制
void 插队(struct Node** 乘务长, int 新成绩) {
    struct Node* 新乘客 = (struct Node*)malloc(sizeof(struct Node));
    if(新乘客 == NULL) {
        printf("完犊子!内存不够了!"); 
        return;
    }
    新乘客->data = 新成绩;
    新乘客->next = *乘务长;  // 新乘客抓住原来车头的袖子
    *乘务长 = 新乘客;       // 乘务长改盯新乘客
}

??场景2:老实人排最后(尾插法)??

c复制
void 排队(struct Node** 乘务长, int 新成绩) {
    struct Node* 新乘客 = 创建节点(新成绩); // 假设已写创建函数
    if(*乘务长 == NULL) {  // 如果火车还没发车
        *乘务长 = 新乘客;
        return;
    }
    
    struct Node* 检票员 = *乘务长;
    while(检票员->next != NULL) {  // 一路小跑到最后一节
        检票员 = 检票员->next;
    }
    检票员->next = 新乘客;  // 把新乘客挂车尾
}

??血泪经验??:

  • 头插法比尾插法??快10倍??(不用遍历整个链表)
  • 但现实开发中尾插法更常用(比如维护聊天记录顺序)

三、删:请走捣蛋鬼学生

(拍桌)现在要删除不及格的学生节点,重点是怎样不断掉整列火车:

c复制
void 开除差生(struct Node** 乘务长, int 分数线) {
    struct Node* 当前车厢 = *乘务长;
    struct Node* 前一节 = NULL;

    while(当前车厢 != NULL) {
        if(当前车厢->data < 分数线) {
            // 分情况处理头节点和中间节点
            if(前一节 == NULL) {  
                *乘务长 = 当前车厢->next;  // 乘务长换盯下一节
            } else {
                前一节->next = 当前车厢->next;
            }
            free(当前车厢);  // 重点!把座位拆了
            当前车厢 = 前一节 == NULL ? *乘务长 : 前一节->next;
        } else {
            前一节 = 当前车厢;
            当前车厢 = 当前车厢->next;
        }
    }
}

??避坑指南??:

  1. 一定要先找到??前驱节点??再删,否则会断链
  2. 删完后记得把??当前指针复位??(很多人栽在这里)
  3. 多条件删除建议用??双指针法??更安全

四、改:给粗心学生改分数

(推眼镜)这个最简单,但有个隐藏巨坑:

c复制
void 改分(struct Node* 乘务长, int 老分数, int 新分数) {
    struct Node* 当前 = 乘务长;
    while(当前 != NULL) {
        if(当前->data == 老分数) {
            当前->data = 新分数;  // 找到就直接改
            // break;  // 看需求决定是否只改第一个
        }
        当前 = 当前->next;
    }
}

??灵魂拷问??:

  • 为什么参数用struct Node*而不是struct Node**
    (答:因为这里不需要改链表结构,只要改数据域)

五、查:找出学霸的位置

(拿望远镜)查操作虽然简单,但效率差异极大:

??方法1:老实人遍历法??

c复制
int 找学霸(struct Node* 乘务长) {
    int 最高分 = -1;
    struct Node* 当前 = 乘务长;
    while(当前 != NULL) {
        if(当前->data > 最高分) {
            最高分 = 当前->data;
        }
        当前 = 当前->next;
    }
    return 最高分;
}

??方法2:外挂记录法??(维护额外指针)

c复制
struct Node* 学霸指针 = NULL;

void 插入时记录(struct Node* 新节点) {
    if(学霸指针 == NULL || 新节点->data > 学霸指针->data) {
        学霸指针 = 新节点;
    }
}

??效率对比??:

  • 遍历法时间复杂度O(n)
  • 记录法查询只要O(1),但增删时要额外维护

六、过来人的碎碎念

(放下粉笔)说点教科书不会告诉你的:

  1. ??画图大法好??:在纸上画出节点和箭头,操作前先模拟指针变化,能避免80%的错误
  2. ??防御性编程??:每个malloc后面都跟个NULL检查,就像上车系安全带
  3. ??内存泄漏检测??:可以用Valgrind工具(新手神器!)
  4. ??链表 vs 数组??:
    • 数据量超500时链表更省内存
    • 但缓存命中率比数组低3-5倍

有次我debug到凌晨3点,最后发现是尾插法忘记处理空链表...所以啊,??写链表时要保持心态稳定??,这玩意儿就是越练越熟的。


??最后送你三个锦囊??:

  1. 复杂操作先写伪代码
  2. 多用辅助指针别心疼
  3. 每个函数最多20行(长了肯定有问题)

下次再被链表虐的时候,记得打开这篇文章——我在这埋了彩蛋(才不告诉你在第7行)!

搜索