几分钟带你秒懂链表!还讲了链表节点设计及创建节点的方法

网安智编 厦门萤点网络科技 2025-12-24 00:07 46 0
链表详解:动态数据结构 链表不是劳力士,是最强动态数据结构 链表是编程劝退知识点? 常见困扰 症状: 今天,几分钟让你秒懂链表! 链表 vs 劳力士 误解 学生: “老师,你说的链表是这个吗?我家里有好多呢。” 老师: “我们说的是编程中的...

链表详解:动态数据结构

链表不是劳力士,是最强动态数据结构

链表是编程劝退知识点? 常见困扰

症状:

今天,几分钟让你秒懂链表!

链表 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;
}

链表遍历 像警察查案

链表遍历就像警察查案:

c 链表类_链表编程劝退知识点_链表详解动态数据结构

头指针:线索的头部第一个节点:第一个嫌疑人嫌疑人交代:”我的同伙在这个地址…”顺藤摸瓜:一个个找出所有嫌疑人 遍历代码

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 数组对比总结

链表的本质:

适用场景:

学习链表后,你已经掌握了数据结构的基础,可以继续学习更复杂的数据结构和算法了!