You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

双向链表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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 11:21:08