双向链表DLLInsert()函数逻辑错误:位置有效性校验问题
修复双向链表
DLLInsert()的位置合法性校验问题 先明确几个关键细节:
- 你的节点结构体里写的
struct DLLNode * head应该是笔误吧?标准双向链表节点需要**next指针指向后继节点**,我会基于这个修正来实现;如果你的设计确实需要每个节点存储头指针,我会在最后说明调整方式。 - 合法的插入位置范围:对于包含
n个节点的链表,插入位置必须是1 ≤ position ≤ n+1(位置1是表头插入,n+1是表尾插入),小于1或大于n+1的位置都是非法的。
现有代码的核心问题
当前的DLLInsert()函数存在两个致命问题:
- 完全没有对
position参数做合法性校验,传入非法位置时会直接引发空指针访问等未定义行为 - 缺少完整的插入逻辑(表头、中间、表尾插入的具体处理),甚至内存分配失败后的错误处理也不完整
修复后的完整实现
#include <stdio.h> #include <stdlib.h> // 修正后的双向链表节点结构体(将原笔误的head改为next) struct DLLNode { int data; struct DLLNode *next; struct DLLNode *prev; }; void DLLInsert(struct DLLNode **head, int data, int position) { int k = 1; struct DLLNode *temp, *newNode; // 1. 分配内存并初始化新节点 newNode = (struct DLLNode *)malloc(sizeof(struct DLLNode)); if (!newNode) { printf("内存分配失败!无法插入新节点\n"); return; } newNode->data = data; newNode->prev = NULL; newNode->next = NULL; // 2. 校验插入位置合法性并处理插入 // 情况1:插入表头(位置1),无需遍历 if (position == 1) { newNode->next = *head; if (*head != NULL) { (*head)->prev = newNode; } *head = newNode; return; } // 情况2:插入位置大于1,先遍历到目标位置的前一个节点 temp = *head; while (temp != NULL && k < position - 1) { temp = temp->next; k++; } // 校验:遍历完链表仍未到达目标位置的前一个节点,说明position非法 if (temp == NULL && k < position - 1) { printf("插入位置%d非法!当前链表仅有%d个节点,合法位置范围是1到%d\n", position, k, k + 1); free(newNode); // 释放已分配的内存,避免内存泄漏 return; } // 3. 执行中间/表尾插入操作 newNode->next = temp->next; newNode->prev = temp; if (temp->next != NULL) { temp->next->prev = newNode; } temp->next = newNode; } // 辅助测试函数:打印链表内容 void printDLL(struct DLLNode *head) { struct DLLNode *temp = head; printf("当前链表:"); while (temp != NULL) { printf("%d ", temp->data); temp = temp->next; } printf("\n"); } // 测试示例 int main() { struct DLLNode *head = NULL; DLLInsert(&head, 10, 1); // 合法:表头插入 DLLInsert(&head, 20, 2); // 合法:表尾插入 DLLInsert(&head, 15, 2); // 合法:中间插入 printDLL(head); // 输出:当前链表:10 15 20 DLLInsert(&head, 30, 5); // 非法:链表仅有3个节点,合法位置到4 DLLInsert(&head, 5, 0); // 非法:位置小于1 return 0; }
关键合法性校验逻辑说明
- 对于
position == 1的情况,直接处理表头插入,这是天然合法的,无需额外遍历校验。 - 对于
position > 1的情况,我们遍历链表直到找到第position-1个节点,或者遍历到链表末尾:- 如果遍历到末尾时,已遍历的节点数
k仍然小于position-1,说明传入的位置超过了链表长度+1,直接判定为非法,释放新节点内存后返回。 - 否则,执行正常的插入操作。
- 如果遍历到末尾时,已遍历的节点数
如果你的节点确实需要存储头指针的调整方式
如果你的节点结构体必须保留head成员(比如特殊设计需求),只需要在初始化新节点时设置newNode->head = *head,其他插入逻辑和校验逻辑保持一致即可:
// 保留原节点结构体设计 struct DLLNode { int data; struct DLLNode *head; // 存储头指针 struct DLLNode *prev; struct DLLNode *next; // 仍需next指针指向后继节点 }; // 初始化新节点时添加头指针赋值 newNode->head = *head;
内容的提问来源于stack exchange,提问作者Snigdh
相关产品推荐
相关产品推荐

