双向链表的基本操作(附C语言完整实现)

简介: 本文面向已掌握双向链表创建的读者,系统讲解其核心操作:在表头、表中、表尾添加节点;删除表头、表中、表尾节点;遍历查找指定元素;以及修改节点数据。附完整C语言实现代码与执行示例。(239字)

本文适合已经学会如何创建一个双向链表的读者,本节学习有关双向链表的一些基本操作,即如何在双向链表中添加、删除、查找或更改数据元素。


本节知识基于已熟练掌握双向链表创建过程的基础上,我们继续上节所创建的双向链表来学习本节内容,创建好的双向链表如图 1 所示:

图 1 双向链表示意图

双向链表添加节点

根据数据添加到双向链表中的位置不同,可细分为以下 3 种情况:

1) 添加至表头

将新数据元素添加到表头,只需要将该元素与表头元素建立双层逻辑关系即可。


换句话说,假设新元素节点为 temp,表头节点为 head,则需要做以下 2 步操作即可:

  1. temp->next=head; head->prior=temp;
  2. 将 head 移至 temp,重新指向新的表头;


例如,将新元素 7 添加至双链表的表头,则实现过程如图 2 所示:

图 2 添加元素至双向链表的表头

2) 添加至表的中间位置

同单链表添加数据类似,双向链表中间位置添加数据需要经过以下 2 个步骤,如图 3 所示:

  1. 新节点先与其直接后继节点建立双层逻辑关系;
  2. 新节点的直接前驱节点与之建立双层逻辑关系;

图 3 双向链表中间位置添加数据元素

3) 添加至表尾

与添加到表头是一个道理,实现过程如下(如图 4 所示):

  1. 找到双链表中最后一个节点;
  2. 让新节点与最后一个节点进行双层逻辑关系;

图 4 双向链表尾部添加数据元素


因此,我们可以试着编写双向链表添加数据的 C 语言代码,参考代码如下:

Line* insertLine(Line* head, int data, int add) {
    //新建数据域为data的结点
    Line* temp = (Line*)malloc(sizeof(Line));
    temp->data = data;
    temp->prior = NULL;
    temp->next = NULL;
    //插入到链表头,要特殊考虑
    if (add == 1) {
        temp->next = head;
        head->prior = temp;
        head = temp;
    }
    else {
        int i;
        Line* body = head;
        //找到要插入位置的前一个结点
        for (i = 1; i < add - 1; i++) {
            body = body->next;
            //只要 body 不存在,表明插入位置输入错误
            if (!body) {
                printf("插入位置有误!\n");
                return head;
            }
        }
        //判断条件为真,说明插入位置为链表尾,实现第 2 种情况
        if (body && (body->next == NULL)) {
            body->next = temp;
            temp->prior = body;
        }
        else {
            //第 2 种情况的具体实现
            body->next->prior = temp;
            temp->next = body->next;
            body->next = temp;
            temp->prior = body;
        }
    }
    return head;
}

双向链表删除节点

和添加结点的思想类似,在双向链表中删除目标结点也分为 3 种情况。

1) 删除表头结点

删除表头结点的过程如下图所示:


图 5 删除双链表表头元素


删除表头结点的实现过程是:

  1. 新建一个指针指向表头结点;
  2. 断开表头结点和其直接后续结点之间的关联,更改 head 头指针的指向,同时将其直接后续结点的 prior 指针指向 NULL;
  3. 释放表头结点占用的内存空间。

2) 删除表中结点

删除表中结点的过程如下图所示:


图 6 删除表中结点

删除表中结点的实现过程是:

  1. 找到目标结点,新建一个指针指向改结点;
  2. 将目标结点从链表上摘除;
  3. 释放该结点占用的内存空间。

3) 删除表尾结点

删除表尾结点的过程如下图所示:


图 7 删除表尾结点


删除表尾结点的实现过程是:

  1. 找到表尾结点,新建一个指针指向该结点;
  2. 断点表尾结点和其直接前驱结点的关联,并将其直接前驱结点的 next 指针指向 NULL;
  3. 释放表尾结点占用的内存空间。


双向链表删除节点的 C 语言实现代码如下:

//删除结点的函数,data为要删除结点的数据域的值
Line* delLine(Line* head, int data) {
    Line* temp = head;
    while (temp) {
        if (temp->data == data) {
            //删除表头结点
            if (temp->prior == NULL) {
                head = head->next;
                if (head) {
                    head->prior = NULL;
                    temp->next = NULL;
                }
                free(temp);
                return head;
            }
            //删除表中结点
            if (temp->prior && temp->next) {
                temp->next->prior = temp->prior;
                temp->prior->next = temp->next;
                free(temp);
                return head;
            }
            //删除表尾结点
            if (temp->next == NULL) {
                temp->prior->next = NULL;
                temp->prior = NULL;
                free(temp);
                return head;
            }
        }
        temp = temp->next;
    }
    printf("表中没有目标元素,删除失败\n");
    return head;
}

双向链表查找节点

通常情况下,双向链表和单链表一样都仅有一个头指针。因此,双链表查找指定元素的实现同单链表类似,也是从表头依次遍历表中元素。


C 语言实现代码为:

//head为原双链表,elem表示被查找元素
int selectElem(line * head,int elem){
//新建一个指针t,初始化为头指针 head
    line * t=head;
    int i=1;
    while (t) {
        if (t->data==elem) {
            return i;
        }
        i++;
        t=t->next;
    }
    //程序执行至此处,表示查找失败
    return -1;
}

双向链表更改节点

更改双链表中指定结点数据域的操作是在查找的基础上完成的。实现过程是:通过遍历找到存储有该数据元素的结点,直接更改其数据域即可。


实现此操作的 C 语言实现代码如下:

//更新函数,其中,add 表示要修改的元素,newElem 为新数据的值
void amendElem(Line* p, int oldElem, int newElem) {
    Line* temp = p;
    int find = 0;
    //找到要修改的目标结点
    while (temp)
    {
        if (temp->data == oldElem) {
            find = 1;
            break;
        }
        temp = temp->next;
    }
    //成功找到,则进行更改操作
    if (find == 1) {
        temp->data = newElem;
        return;
    }
    //查找失败,输出提示信息
    printf("链表中未找到目标元素,更改失败\n");
}

总结

这里给出双链表中对数据进行 "增删查改" 操作的完整实现代码:

#include <stdio.h>
#include <stdlib.h>
typedef struct line {
    struct line* prior; //指向直接前趋
    int data;
    struct line* next; //指向直接后继
}Line;

Line* initLine(Line* head) {
    int i;
    Line* list = NULL;
    head = (Line*)malloc(sizeof(Line));//创建链表第一个结点(首元结点)
    head->prior = NULL;
    head->next = NULL;
    head->data = 1;
    list = head;
    for (i = 2; i <= 5; i++) {
        //创建并初始化一个新结点
        Line* body = (Line*)malloc(sizeof(Line));
        body->prior = NULL;
        body->next = NULL;
        body->data = i;
        //直接前趋结点的next指针指向新结点
        list->next = body;
        //新结点指向直接前趋结点
        body->prior = list;
        list = list->next;
    }
    return head;
}

void display(Line* head) {
    Line* temp = head;
    while (temp) {
        //如果该节点无后继节点,说明此节点是链表的最后一个节点
        if (temp->next == NULL) {
            printf("%d\n", temp->data);
        }
        else {
            printf("%d <-> ", temp->data);
        }
        temp = temp->next;
    }
}

//删除结点的函数,data为要删除结点的数据域的值
Line* delLine(Line* head, int data) {
    Line* temp = head;
    while (temp) {
        if (temp->data == data) {
            //删除表头结点
            if (temp->prior == NULL) {
                head = head->next;
                if (head) {
                    head->prior = NULL;
                    temp->next = NULL;
                }
                free(temp);
                return head;
            }
            //删除表中结点
            if (temp->prior && temp->next) {
                temp->next->prior = temp->prior;
                temp->prior->next = temp->next;
                free(temp);
                return head;
            }
            //删除表尾结点
            if (temp->next == NULL) {
                temp->prior->next = NULL;
                temp->prior = NULL;
                free(temp);
                return head;
            }
        }
        temp = temp->next;
    }
    printf("表中没有目标元素,删除失败\n");
    return head;
}

//head为原双链表,elem表示被查找元素
int selectElem(Line* head, int elem) {
    //新建一个指针t,初始化为头指针 head
    Line* t = head;
    int i = 1;
    while (t) {
        if (t->data == elem) {
            return i;
        }
        i++;
        t = t->next;
    }
    //程序执行至此处,表示查找失败
    return -1;
}

//更新函数,其中,add 表示要修改的元素,newElem 为新数据的值
void amendElem(Line* p, int oldElem, int newElem) {
    Line* temp = p;
    int find = 0;
    //找到要修改的目标结点
    while (temp)
    {
        if (temp->data == oldElem) {
            find = 1;
            break;
        }
        temp = temp->next;
    }
    //成功找到,则进行更改操作
    if (find == 1) {
        temp->data = newElem;
        return;
    }
    //查找失败,输出提示信息
    printf("链表中未找到目标元素,更改失败\n");
}
//释放链表中结点占用的内存空间
void free_line(Line* head) {
    Line* temp = head;
    while (temp) {
        head = head->next;
        free(temp);
        temp = head;
    }
}

int main()
{
    //创建一个头指针
    Line* head = NULL;
    //调用链表创建函数
    head = initLine(head);
    printf("创建好的双向链表为:\n");
    display(head);

    printf("删除元素 2:\n");
    head = delLine(head, 2);
    display(head);

    printf("元素 3 的位置是:%d\n", selectElem(head, 3));
  
    printf("表中的元素 3 改为 6:\n");
    amendElem(head, 3, 6);
    display(head);

    free_line(head);
    return 0;
}

程序执行结果为:

创建好的双向链表为:

1 <-> 2 <-> 3 <-> 4 <-> 5

删除元素 2:

1 <-> 3 <-> 4 <-> 5

元素 3 的位置是:2

表中的元素 3 改为 6:

1 <-> 6 <-> 4 <-> 5

相关文章
|
22天前
|
人工智能 Linux iOS开发
Ollama本地部署教程|Ollama官网下载安装图文(附热门模型清单)
Ollama 是一款免费开源的本地大模型运行工具,支持 Windows/macOS/Linux,可离线运行 Llama、Qwen、DeepSeek 等主流模型。数据不出本地,隐私安全;命令行+API 设计,适合开发者集成。模型下载快、生态丰富,是隐私敏感用户和本地 AI 开发者的首选。(239 字)
|
3月前
|
存储 C# C语言
VS2017下载地址和安装使用图文教程(附官网安装包)
微软VS2017是功能强大的集成开发环境,支持C/C++、C#、Python等多语言及iOS/Android跨平台开发。提供社区版(免费)、专业版和企业版三类授权,推荐初学者使用社区版。本文详述了.NET Framework安装、VS2017下载配置、C语言项目创建、编译链接及调试全流程,含图文步骤与实用技巧。(239字)
|
6天前
|
JSON 人工智能 测试技术
别再给大模型输出写死期望值:Hypothesis + Pydantic + pytest 把非确定性回答测成一组『不变量』
本文揭示大模型测试中“固定值断言”的致命缺陷:因模型输出天然非确定(字段顺序、类型漂移、冗余文本等),`assert == 固定字典` 导致假红或漏检。提出用属性测试(Hypothesis + Pydantic)替代——聚焦守业务不变量(如金额非负、必填字段存在、不泄露提示),而非形态一致。解耦“输出长什么样”与“输出对不对”,让测试真正守住底线。
别再给大模型输出写死期望值:Hypothesis + Pydantic + pytest 把非确定性回答测成一组『不变量』
|
16天前
|
机器学习/深度学习 缓存 人工智能
Claude Fable 5.1 发布解读:科研智能体跑分翻倍,Agent 成本降45%
Claude Fable 5.1 于 2026 年 9 月发布:科研智能体基准翻倍、缓存降价 75%,Agent 成本最高降 45%,一文讲清升级点与价格。
254 1
Claude Fable 5.1 发布解读:科研智能体跑分翻倍,Agent 成本降45%
|
8天前
|
人工智能 Ubuntu 数据可视化
Ubuntu 本地部署Ollama+OpenWebUI教程
本文详解Ubuntu下零基础部署Ollama+OpenWebUI:一键安装Ollama(支持CPU/GPU)、拉取轻量qwen3.5:0.8b模型、虚拟环境隔离安装OpenWebUI、配置systemd自启服务,最终建成可远程访问、离线运行、稳定易用的本地AI对话网页。
199 2
|
12天前
|
JavaScript 开发者
DSH plugin 从零怎么写?最小插件目录、本地构建与装进 profile 调试的完整起步流程
写第一个 DSH plugin 只需要一个导出 apply 函数的模块:先用 patch 覆盖层把本地文件插进 Web 界面验证,再做成包用 dsh plugin --profile add 装进 profile,最后用 --dump-config 与日志排错。
135 2
|
3月前
|
人工智能 JSON Linux
CC Switch安装:AI 编程工具统一管理平台,告别手动改配置文件(2026最新)
CC-Switch 是一款开源跨平台AI编程工具配置管理器,支持Claude Code、Codex、Gemini CLI等7款主流工具。v3.16.3版提供图形化API切换、MCP管理、Skills插件、用量统计与健康检查,告别手动改JSON,本地加密存储,安全高效。(239字)
|
2月前
|
Linux 开发工具 iOS开发
【2026实测】Homebrew安装+配置国内源+使用一篇搞定(多种安装方式)
Homebrew 是 macOS 最流行的开源包管理器,被誉为“macOS 缺失的包管理器”。一行命令即可安装、更新、卸载命令行工具(如 Git、Python)和图形应用(如 Chrome、VS Code),自动处理依赖与环境配置,大幅提升开发效率。(239 字)
|
1月前
|
Java 测试技术 Linux
JMeter下载、安装、汉化、插件配置、压测一篇搞定(附官网安装包)
Apache JMeter 是 Apache 基金会推出的免费开源性能测试工具,支持跨平台(Windows/macOS/Linux),可模拟高并发用户对Web/接口进行压力测试,具备功能全面、插件生态丰富、学习成本低等优势,是全球最主流的压测工具之一。(239字)

热门文章

最新文章