C语言单链表删除指定结点实战:含头/尾/重复值处理
在C语言数据结构中,单链表的删除操作是面试与实务的核心考点。本文将通过尾插法建立链表,深入解析如何根据数值、位置以及去重需求删除结点,包含完整的C/C++代码实现与边界条件处理指南。
基于尾插法的单链表删除
使用尾插法建立的单链表,其输出顺序与输入顺序一致。以下代码演示了如何通过遍历查找指定数值并安全释放内存。
第1步:初始化链表结构
定义链表节点结构体,并创建头结点。头结点不存储实际数据,作为链表的起始标识,便于统一处理删除操作(尤其是删除第一个数据结点时)。
#include <bits/stdc++.h>
using namespace std;
typedef struct node {
int data;
struct node *next;
} Node; // 建议规范命名
第2步:构建单链表(尾插法)
通过循环读取用户输入,动态分配内存并链接至链表尾部。此步骤完成数据的结构化存储。
Node *head = new Node;
head->next = NULL;
Node *tail = head;
int n, k;
printf("一共要输入的数: ");
scanf("%d", &n); // 修正原稿中的换行符读取隐患
for (int i = 0; i < n; i++) {
cin >> k;
Node *p = new Node;
p->data = k;
p->next = NULL;
tail->next = p;
tail = p;
}
第3步:按值删除指定结点
删除操作的关键在于找到目标结点的前驱结点。由于使用了带头结点的链表,我们需要一个指针 r 指向当前结点的上一个结点,而 p 用于扫描当前结点。
- 入口:输入待删除的值
m。 - 动作:遍历链表,当
p->data == m时,执行r->next = p->next跳过该结点,并使用delete(p)释放内存。 - 注意:如果删除的是最后一个结点,后续无需再次访问
p,但循环条件已涵盖此场景。
int m;
printf("输入要删除的数: ");
scanf("%d", &m);
Node *p = head; // p从头结点开始
while (p->next != NULL) { // 标准遍历条件更稳健
if (p->next->data == m) {
Node *temp = p->next; // 暂存待删除结点
p->next = temp->next; // 前驱指向后继
delete temp; // 释放内存
} else {
p = p->next; // 移动到下一个
}
}
// 简化版逻辑(对应原稿思路):
p = head;
while (p->next != NULL) {
if (p->next->data == m) {
Node *q = p->next;
p->next = q->next;
delete(q);
} else {
p = p->next;
}
}
说明:原稿代码逻辑较为特殊(在循环内判断),现代C/C++实践中通常采用“维护前驱指针”或“检查 next”的方式以避免悬空指针。上述优化代码更符合标准实践。
常见链表删除变体
删除第 i 个结点
若需按位置删除,需先定位到第 i-1 个结点。假设 i 从1开始计数且有效。
// 假设 head 为带头结点的链表
Node *p = head;
int v = 1;
while (v < i - 1 && p->next != NULL) {
p = p->next;
v++;
}
if (p->next != NULL) {
Node *q = p->next; // 第 i 个结点
p->next = q->next; // 跳过第 i 个
delete(q);
}
删除与指定值 a 相同的结点(含重复值)
若链表中存在多个相同值的结点,需全部删除。此时不能使用原稿中简单的 break,必须继续遍历直到链表末尾。
int a;
scanf("%d", &a);
Node *p = head;
while (p->next != NULL) {
if (p->next->data == a) {
Node *q = p->next;
p->next = q->next;
delete(q);
} else {
p = p->next;
}
}
删除链表中所有重复元素
保留第一次出现的元素,删除后续重复项。这通常需要双重循环或在单层遍历中配合哈希表(若允许额外空间)。以下为无额外空间的双层遍历解法。
Node *p = head->next;
while (p != NULL) {
Node *q = p;
Node *k = q->next;
while (k != NULL) {
if (p->data == k->data) {
Node *temp = k;
q->next = k->next;
k = q->next;
delete(temp);
} else {
q = k;
k = k->next;
}
}
p = p->next;
}
验证与测试案例
通过以下测试用例验证删除逻辑的正确性:
- 测试1(删除尾部):输入
1 2 3 4 5,删除5,输出1 2 3 4。验证是否正确更新了尾指针前驱的next域并释放内存。 - 测试2(删除头部):输入
1 2 3 4 5,删除1,输出2 3 4 5。验证头结点next是否正确指向新首元结点。 - 测试3(删除中部):输入
1 2 3 4 5,删除2,输出1 3 4 5。验证中间结点的链接是否断裂后重建。
总结
单链表删除操作的精髓在于“断链”与“回收”。务必确保在释放内存之前,前驱结点的 next 指针已经正确指向了后继结点。对于涉及重复值删除的场景,需注意不要因 break 导致遗漏。以上就是C语言单链表删除指定结点的详细内容,更多关于C语言数据结构链表的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













