双向链表的基本操作(附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

相关文章
|
1天前
|
存储 C# C语言
VS2017下载地址和安装使用图文教程(附官网安装包)
微软VS2017是功能强大的集成开发环境,支持C/C++、C#、Python等多语言及iOS/Android跨平台开发。提供社区版(免费)、专业版和企业版三类授权,推荐初学者使用社区版。本文详述了.NET Framework安装、VS2017下载配置、C语言项目创建、编译链接及调试全流程,含图文步骤与实用技巧。(239字)
|
1天前
|
C# C语言 C++
VS2019下载地址和安装使用图文教程(附官网安装包)
Visual Studio 2019是微软2019年发布的稳定IDE,支持C/C++、C#等语言。Community版免费且功能完整,安装轻量、兼容性强,尤其适合老项目维护与低配设备。文中详述了下载、安装及用其编写运行C程序的完整流程。(239字)
|
1天前
|
人工智能 JSON Linux
CC Switch安装:AI 编程工具统一管理平台,告别手动改配置文件(2026最新)
CC-Switch 是一款开源跨平台AI编程工具配置管理器,支持Claude Code、Codex、Gemini CLI等7款主流工具。v3.16.3版提供图形化API切换、MCP管理、Skills插件、用量统计与健康检查,告别手动改JSON,本地加密存储,安全高效。(239字)
|
1天前
|
人工智能 运维 安全
员工数据泄露驱动精准钓鱼攻击演化与企业闭环防御技术研究
本文基于SpyCloud 2026年报告(86%财富100强企业存在员工数据泄露),系统剖析泄露数据驱动AI精准钓鱼的全链路机理,提出URL结构、文本语义、页面相似度等五层融合检测模型,准确率提升32.7%;并构建“预警—检测—加固—管控—运营”五位一体闭环防御体系,附可落地Python代码,助力企业应对定向鱼叉攻击。
37 2
|
1天前
|
人工智能 自然语言处理 前端开发
万小智 AI 建站:开发者从零到生产级官网的完全实战指南
本文面向后端/全栈开发者、技术负责人及独立开发者,提供“AI建站+定制开发+生产运维”一体化工作流。10分钟生成官网骨架,支持代码嵌入、域名备案、Webhook集成与多语言部署,大幅降低交付与维护成本,让开发者专注核心业务逻辑。(239字)
|
1天前
|
算法 安全 API
《淘宝开放平台TOP API接入全指南:注册、AppKey获取、签名算法与沙箱调试(2026)》(附python源码)
淘宝开放平台(TOP)是淘宝/天猫官方API体系,区别于1688。本文涵盖注册、AppKey获取、MD5/HmacMD5签名(ASCII排序+首尾拼Secret)、沙箱调试(`tbsandbox.com`网关)及Python可运行示例,含`taobao.item.get`调通验证与避坑指南。(239字)
|
1天前
|
前端开发 开发工具 开发者
GLM-5.2登陆阿里云百炼免费体验:百万Token上下文+Claude级编程,国产大模型又进化了
智谱GLM-5.2正式登陆阿里云百炼!支持百万Token超长上下文,Coding能力达Claude Opus 4.8水准,在FrontierSWE、Terminal-Bench等权威评测中表现优异。开源MIT协议,开箱即用,享百万免费Tokens及弹性计费方案。在阿里云百炼官网:https://t.aliyun.com/U/fPVHqY 免费领取千万Tokens
105 0
|
1天前
|
前端开发 开发工具 开发者
GLM-5.2上线阿里云百炼,即开即用,百万Token上下文、编程媲美Claude
智谱最新旗舰模型GLM-5.2已上架阿里云百炼!支持1M超长上下文,Coding能力跃居开源SOTA,在FrontierSWE、Terminal-Bench等权威评测中媲美Claude Opus 4.8。现开放百万免费Tokens,开箱即用,零门槛体验顶尖长程任务与全链路编程交付能力。在阿里云百炼官网:https://t.aliyun.com/U/fPVHqY 免费领取千万Tokens
79 0
|
1天前
|
人工智能 缓存 自然语言处理
阿里云百炼Token Plan标准/高级/尊享坐席怎么选?Token Plan额度、计费与成本效益全面分析
阿里云百炼Token Plan是面向团队的AI大模型订阅服务,以Credits作为统一计量积分,覆盖文本生成、图像生成、代码开发等全场景模型调用,兼容主流AI编程与智能体工具,提供团队管理、预算管控、数据安全等企业级能力。该方案采用包月订阅制,提供标准、高级、尊享三档坐席,用户按月付费获取固定Credits额度,所有模型调用、工具使用、上下文缓存均按统一规则折算Credits扣除,告别传统按量付费的账单波动,实现AI使用成本的精准可控。本文将从资费额度、Token消耗规则、核心权益、性价比四个维度,全面对比三大坐席差异,为不同使用强度的团队提供清晰的选型参考。
|
1天前
|
机器学习/深度学习 编解码 人工智能
昆虫目标检测数据集:102类别、30,000+张图像 | 目标检测
本数据集含102类昆虫、34156张高质量图像,YOLO格式标注,覆盖稻麦豆果蔬等主要害虫,具备多角度、多光照、多背景及丰富姿态的强多样性,专为小目标、细粒度、多类别农业虫害检测优化,支持YOLOv5-v11等主流模型直接训练。(239字)
96 0