几分钟带你秒懂链表!还讲了链表节点设计及创建节点的方法
链表详解:动态数据结构
链表不是劳力士,是最强动态数据结构
链表是编程劝退知识点? 常见困扰
症状:
今天,几分钟让你秒懂链表!
链表 vs 劳力士 误解
学生: “老师,你说的链表是这个吗?我家里有好多呢。”
老师: “我们说的是编程中的链表,不是世界名表。不过你要是多的话,可以送我几个哟。”
问题引入 需求
从控制台输入不定个数的学生信息
关键: 数量不确定!
数组方案的问题
Student students[100]; // 定100个?
问题:
水货代码鉴定完毕!
链表的解决方案 核心思想
动态增长 = 结构体 + 指针
结构体自引用
问题: 结构体里面能定义它本身的指针吗?
答案: 当然可以!指针只是个地址变量!
链表节点设计 学生节点
typedef struct Student {
char name[20]; // 姓名
int age; // 年龄
struct Student *next; // 指向下一个学生
} Student;
关键: next 指针指向下一个节点!
内存布局(32位程序)
┌────────────┬────┬──────┐
│ name[20] │age │ next │
│ 20字节 │4字节│4字节 │
└────────────┴────┴──────┘
数据部分 指针部分
总大小: 28字节(考虑内存对齐)
创建第一个节点 代码
#include
Student *head = (Student *)malloc(sizeof(Student));
分解:
(()):分配一个大小的内存( *):强制转换为指针类型head:保存首地址
计算类型占用字节数:
sizeof(int) // 4字节
sizeof(char) // 1字节
sizeof(Student) // 28字节
sizeof(int *) // 4字节(32位)或 8字节(64位)
连接节点 创建第二个节点
head->next = (Student *)malloc(sizeof(Student));
现在:
┌────────┐ ┌────────┐
│ head │ ---> │ node1 │
│ node1 │ │ next │ ---> NULL
└────────┘ └────────┘
创建第三个节点
head->next->next = (Student *)malloc(sizeof(Student));
现在:
┌────────┐ ┌────────┐ ┌────────┐
│ head │ ---> │ node1 │ ---> │ node2 │
│ node1 │ │ next │ │ next │ ---> NULL
└────────┘ └────────┘ └────────┘
像糖葫芦一样串起来了!
也像劳力士手表串起来!
循环创建节点 动态增长
Student *head = NULL;
Student *tail = NULL;
for (int i = 0; i < n; i++) {
// 创建新节点
Student *newNode = (Student *)malloc(sizeof(Student));
newNode->next = NULL;
// 输入数据
scanf("%s %d", newNode->name, &newNode->age);
// 连接到链表
if (head == NULL) {
head = newNode; // 第一个节点
tail = newNode;
} else {
tail->next = newNode; // 接到尾部
tail = newNode; // 更新尾部
}
}
完美实现动态增长!
完整示例:学生信息管理 完整代码
#include
#include
#include
// 学生结构体
typedef struct Student {
char name[20];
int age;
struct Student *next;
} Student;
// 添加学生
Student* addStudent(Student *head, char *name, int age) {
// 创建新节点
Student *newNode = (Student *)malloc(sizeof(Student));
strcpy(newNode->name, name);
newNode->age = age;
newNode->next = NULL;
// 如果链表为空
if (head == NULL) {
return newNode;
}
// 找到最后一个节点
Student *current = head;
while (current->next != NULL) {
current = current->next;
}
// 连接新节点
current->next = newNode;
return head;
}
// 显示所有学生
void displayStudents(Student *head) {
if (head == NULL) {
printf("链表为空\n");
return;
}
printf("\n学生列表:\n");
printf("================================\n");
Student *current = head;
int index = 1;
while (current != NULL) {
printf("学生%d:%s, %d岁\n", index, current->name, current->age);
current = current->next;
index++;
}
printf("================================\n");
}
// 释放链表
void freeList(Student *head) {
Student *current = head;
while (current != NULL) {
Student *temp = current;
current = current->next;
free(temp);
}
}
int main() {
Student *head = NULL;
int choice;
char name[20];
int age;
while (1) {
printf("\n===== 学生信息管理系统 =====\n");
printf("1. 添加学生\n");
printf("2. 查看学生\n");
printf("3. 退出\n");
printf("请选择:");
scanf("%d", &choice);
switch (choice) {
case 1:
printf("请输入学生姓名:");
scanf("%s", name);
printf("请输入学生年龄:");
scanf("%d", &age);
head = addStudent(head, name, age);
printf("添加成功!\n");
break;
case 2:
displayStudents(head);
break;
case 3:
freeList(head);
printf("再见!\n");
return 0;
default:
printf("无效选择!\n");
}
}
return 0;
}
链表遍历 像警察查案
链表遍历就像警察查案:

头指针:线索的头部第一个节点:第一个嫌疑人嫌疑人交代:”我的同伙在这个地址…”顺藤摸瓜:一个个找出所有嫌疑人 遍历代码
Student *current = head;
while (current != NULL) {
// 处理当前节点
printf("%s, %d岁\n", current->name, current->age);
// 移动到下一个节点
current = current->next;
}
关键:
单链表 特点
单链表:
结构图
┌──────┐ ┌──────┐ ┌──────┐ ┌──────┐
│ head │───>│ node1│───>│ node2│───>│ node3│───> NULL
└──────┘ │ next │ │ next │ │ next │
└──────┘ └──────┘ └──────┘
像串糖葫芦!
循环链表 特点
循环链表:
结构图
┌─────────────────────────┐
│ │
↓ │
┌──────┐ ┌──────┐ ┌──────┐ │
│ head │───>│ node1│───>│ node2│──┘
└──────┘ │ next │ │ next │
└──────┘ └──────┘
实现要点
// 创建循环链表
tail->next = head; // 尾部指向头部
// 遍历循环链表
Student *current = head;
do {
printf("%s\n", current->name);
current = current->next;
} while (current != head);
双向链表 特点
双向链表:
结构图
┌──────┐ ┌──────┐ ┌──────┐
NULL <───│ node1│<──>│ node2│<──>│ node3│───> NULL
│ prev │ │ prev │ │ prev │
│ next │ │ next │ │ next │
└──────┘ └──────┘ └──────┘
节点定义
typedef struct Student {
char name[20];
int age;
struct Student *prev; // 指向前一个
struct Student *next; // 指向后一个
} Student;
插入节点
void insertNode(Student *current, Student *newNode) {
newNode->next = current->next;
newNode->prev = current;
if (current->next != NULL) {
current->next->prev = newNode;
}
current->next = newNode;
}
链表常见操作 插入节点
头部插入:
Student* insertAtHead(Student *head, Student *newNode) {
newNode->next = head;
return newNode;
}
尾部插入:
void insertAtTail(Student *head, Student *newNode) {
Student *current = head;
while (current->next != NULL) {
current = current->next;
}
current->next = newNode;
newNode->next = NULL;
}
删除节点
Student* deleteNode(Student *head, char *name) {
if (head == NULL) return NULL;
// 删除头节点
if (strcmp(head->name, name) == 0) {
Student *temp = head;
head = head->next;
free(temp);
return head;
}
// 删除其他节点
Student *current = head;
while (current->next != NULL) {
if (strcmp(current->next->name, name) == 0) {
Student *temp = current->next;
current->next = temp->next;
free(temp);
break;
}
current = current->next;
}
return head;
}
查找节点
Student* findNode(Student *head, char *name) {
Student *current = head;
while (current != NULL) {
if (strcmp(current->name, name) == 0) {
return current;
}
current = current->next;
}
return NULL;
}
本文要点回顾 记忆口诀
链表好比糖葫芦,节点串起一条路。
分配新节点,指针连接不会断。
单链只能往前走,双链前后都自由。
循环链表成闭环,遍历一圈回起点。
实战练习练习1:反转链表
任务: 将链表反转
点击查看答案
Student* reverseList(Student *head) {
Student *prev = NULL;
Student *current = head;
Student *next = NULL;
while (current != NULL) {
next = current->next; // 保存下一个
current->next = prev; // 反转指针
prev = current; // 前进
current = next;
}
return prev; // 新的头节点
}
练习2:查找中间节点
任务: 找到链表的中间节点
点击查看答案
Student* findMiddle(Student *head) {
if (head == NULL) return NULL;
Student *slow = head;
Student *fast = head;
// 快慢指针
while (fast != NULL && fast->next != NULL) {
slow = slow->next;
fast = fast->next->next;
}
return slow;
}
练习3:检测环
任务: 判断链表是否有环
点击查看答案
int hasCycle(Student *head) {
if (head == NULL) return 0;
Student *slow = head;
Student *fast = head;
while (fast != NULL && fast->next != NULL) {
slow = slow->next;
fast = fast->next->next;
if (slow == fast) {
return 1; // 有环
}
}
return 0; // 无环
}
互动时间
思考题:
链表和数组有什么区别?各有什么优缺点?为什么链表需要手动释放内存?如何判断链表是否有环?
如果本文对你有帮助,欢迎:
—-本文为”C++ 大白话”系列第 20 篇
链表 vs 数组对比总结
链表的本质:
适用场景:
学习链表后,你已经掌握了数据结构的基础,可以继续学习更复杂的数据结构和算法了!
























