是不是很多零基础学数据结构的朋友,一提到用 C 语言写链表就发怵?听别人说 “链表要学指针,很难”,还没开始就打退堂鼓;好不容易看懂了原理,自己动手写代码却到处报错,要么是指针指向不对,要么是内存分配出问题;更头疼的是写出来的链表增删功能,运行时要么没反应,要么直接崩溃。其实啊,链表是数据结构的入门基础,用 C 语言实现没那么难,只要跟着案例一步步做,零基础也能学会。今天兔子哥就带大家从最基础的节点创建开始,手把手教你实现链表的增删改查,实战案例全解析,新手跟着练,链表代码轻松写出来,一起往下看吧!
说到链表,你知道它和数组有啥区别吗?为啥学数据结构非得学链表?简单说,数组就像一排连续的盒子,每个盒子有固定位置,想在中间加个盒子得挪动后面所有的;而链表就像串珠子,每个珠子(节点)用线(指针)连起来,加珠子时只要把线重新接一下就行,不用挪动其他珠子。这就是链表的优势:增删数据特别方便,尤其数据量大的时候,比数组效率高多了。
咱们先看个对比表,感受下数组和链表的区别,就知道为啥要学链表了:
| 操作类型 | 数组实现 | 链表实现 |
|---|---|---|
| 随机访问 | 快(直接用索引) | 慢(得从头找) |
| 中间插入数据 | 慢(要挪动后面所有元素) | 快(改指针指向就行) |
| 内存占用 | 连续空间,可能浪费内存 | 分散空间,用多少占多少 |
要实现链表,第一步得定义 “节点”。节点就像珠子,每个珠子有两部分:装数据的 “数据域” 和连线的 “指针域”(指向下一个珠子)。用 C 语言定义节点特别简单:
c
// 链表节点定义,零基础必须记住这个结构typedef struct Node {int data; // 数据域,存具体数值struct Node* next; // 指针域,指向下一个节点} Node;这里要注意,
struct Node* next这个写法,很多新手容易漏写struct,导致编译错误。记住,C 语言里结构体内部引用自己,必须加struct关键字。有了节点定义,接下来得写个创建节点的函数,就像工厂造珠子一样:
c
// 创建新节点的函数Node* createNode(int data) {// 申请内存空间,用malloc函数Node* newNode = (Node*)malloc(sizeof(Node));// 一定要检查内存是否申请成功,不然容易崩溃if (newNode == NULL) {printf("哎呀,内存不够了,创建节点失败!\n");return NULL;}// 给数据域赋值newNode->data = data;// 新节点刚开始没有下一个,指针设为NULLnewNode->next = NULL;return newNode;}这段代码里,
malloc和NULL检查特别重要,很多新手刚开始学容易漏掉,结果程序动不动就崩溃,还不知道为啥。记住,用 C 语言写链表,内存操作一定要小心。节点创建好了,怎么把它们串成链表呢?咱们先实现 “在链表末尾添加节点” 的功能,就像在珠子串后面加新珠子:
c
// 向链表末尾添加节点void appendNode(Node** head, int data) {// 创建新节点Node* newNode = createNode(data);if (newNode == NULL) return;// 如果链表是空的(头指针为NULL),新节点就是头节点if (*head == NULL) {*head = newNode;return;}// 不是空链表,就找到最后一个节点Node* current = *head;// 一直往后找,直到next为NULL,就是最后一个节点while (current->next != NULL) {current = current->next;}// 把最后一个节点的next指向新节点current->next = newNode;}这里有个难点:函数参数用了
Node** head(二级指针)。为啥不用一级指针?因为咱们可能要修改头指针本身(比如空链表添加第一个节点时),一级指针传进去改不了原值,二级指针才能真正修改头指针。新手刚开始可能不理解,多敲几遍代码试试就知道了。有了添加功能,还得能遍历链表,看看里面有啥数据。遍历就是从头节点开始,一个个往后找,直到指针为 NULL:
c
// 遍历链表并打印数据void printList(Node* head) {// 如果链表为空,提示一下if (head == NULL) {printf("链表是空的哦,啥都没有~\n");return;}Node* current = head;printf("链表数据:");// 循环遍历每个节点while (current != NULL) {printf("%d ", current->data);// 移到下一个节点current = current->next;}printf("\n");}这段代码比较简单,就是用
while循环跟着指针走,把每个节点的数据打印出来。测试的时候经常用这个函数,看看链表有没有按预期创建成功。接下来实现 “删除节点” 功能,这是链表的核心优势所在。比如要删除指定数值的节点,步骤是:找到要删的节点,把它前面节点的指针指向它后面的节点,再释放它的内存。
c
// 删除指定数据的节点void deleteNode(Node** head, int data) {// 如果链表为空,直接返回if (*head == NULL) {printf("链表是空的,没法删除呀~\n");return;}Node* temp = *head;Node* prev = NULL;// 先检查头节点是不是要删的if (temp != NULL && temp->data == data) {*head = temp->next; // 头指针指向第二个节点free(temp); // 释放内存,不然会内存泄漏printf("删除成功!\n");return;}// 不是头节点,就找要删除的节点和它前面的节点while (temp != NULL && temp->data != data) {prev = temp;temp = temp->next;}// 如果没找到要删除的节点if (temp == NULL) {printf("没找到要删除的数据 %d 哦~\n", data);return;}// 把前面节点的next指向后面节点,跳过要删除的节点prev->next = temp->next;// 释放内存free(temp);printf("删除成功!\n");}删除节点时一定要记得
free释放内存,不然会造成内存泄漏,程序运行久了就会变慢甚至崩溃。这也是 C 语言开发必须注意的细节。现在咱们把这些功能串起来,写个完整的测试程序:
c
int main() {Node* head = NULL; // 头指针初始为空// 添加几个节点appendNode(&head, 10);appendNode(&head, 20);appendNode(&head, 30);printList(head); // 应该输出:10 20 30// 删除中间的20deleteNode(&head, 20);printList(head); // 应该输出:10 30// 再添加一个40appendNode(&head, 40);printList(head); // 应该输出:10 30 40// 释放整个链表的内存(避免内存泄漏)Node* current = head;while (current != NULL) {Node* next = current->next;free(current);current = next;}head = NULL; // 头指针设为空,避免野指针return 0;}运行这个程序,如果输出和预期一致,说明你的链表实现成功了!新手刚开始可能会遇到各种小错误,比如指针没赋 NULL 导致遍历死循环,或者删除节点后没处理头指针,多调试几次就能解决。
你可能会问:“学这些基础操作有啥用?考研面试会考这么简单的吗?” 其实啊,复杂的链表操作都是从这些基础来的。比如面试常考的 “反转链表”“检测链表是否有环”,本质上还是指针操作,把基础练熟了,复杂问题自然能迎刃而解。
兔子哥刚开始学的时候,写删除节点的代码总漏掉释放内存,后来调试时看到内存占用越来越大,才明白
free的重要性。还有指针指向的问题,经常搞不清current和current->next的区别,后来画了很多示意图,才慢慢理顺。其实链表就像串珠子,多动手串几次,感觉自然就来了。建议新手把今天的代码抄下来,自己改改数据、加加功能,比如试试在指定位置插入节点,或者实现链表排序,练得多了就会发现,C 语言链表没那么难,甚至还挺有意思的。
最后想说,数据结构入门别怕难,从链表这种基础结构开始,一步一步来,把每个小功能实现好,积累起来就能掌握更复杂的结构。遇到问题多画图、多调试,你会发现指针操作其实很直观,链表也能成为你的强项!
标签: 数据结构 createNode
版权声明:除非特别标注,否则均为本站原创文章,转载时请以链接形式注明文章出处。
还木有评论哦,快来抢沙发吧~