C语言/数据结构——(用双链表实现数据的增删查改)

一.前言

嗨嗨嗨,大家好久不见!前面我们已经通过数组实现数据的增删查改、单链表实现数据的增删查改,现在让我们尝试一下使用双链表实现数据的增删查改吧!

二.正文

如同往常一样,对于稍微大点的项目来说,我们使用多文件操作更好一些。

List.h

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int DataType;
struct ListNode
{DataType data;struct ListNode* prev;struct ListNode* next;
};
typedef struct ListNode ListNode;
void ListInit(ListNode** pphead);//双向链表初始化
void ListDestroy(ListNode* phead);//双向链表销毁
void ListPrint(ListNode* phead);//双向链表打印
void ListPushBack(ListNode* phead, DataType x);//双向链表尾部插入节点
void ListPushFront(ListNode* phead, DataType x);//双向链表第一个有效节点前插入节点(即哨兵位的后一个节点为第一个有效节点)
void ListPopBack(ListNode* phead);//双向链表删除尾部节点
void ListPopFront(ListNode* phead);//双向链表删除第一个有效节点
void ListInsert(ListNode* pos, DataType x);//双向链表指定位置之后插入节点
void ListErase(ListNode* pos);//双向链表指定位置之后删除节点
ListNode* ListFind(ListNode* phead, DataType x);//查找目标节点

我们首先定义了这个项目的头文件,并把其他函数实现过程中可能使用的头文件包含在List.h中,这样就可以方便代码的维护,并且减少冗余的代码。

这是我们的双向链表结构体的定义:

struct ListNode
{DataType data;struct ListNode* prev;struct ListNode* next;
};

和单链表区分的话,那就是单链表定义的结构体中没有指向前一个节点的prev。

下面是我们在实现整个项目中,各种功能的罗列,就类似于书的目录一样。

void ListInit(ListNode** pphead);//双向链表初始化
void ListDestroy(ListNode* phead);//双向链表销毁
void ListPrint(ListNode* phead);//双向链表打印
void ListPushBack(ListNode* phead, DataType x);//双向链表尾部插入节点
void ListPushFront(ListNode* phead, DataType x);//双向链表第一个有效节点前插入节点(即哨兵位的后一个节点为第一个有效节点)
void ListPopBack(ListNode* phead);//双向链表删除尾部节点
void ListPopFront(ListNode* phead);//双向链表删除第一个有效节点
void ListInsert(ListNode* pos, DataType x);//双向链表指定位置之后插入节点
void ListErase(ListNode* pos);//双向链表指定位置之后删除节点
ListNode* ListFind(ListNode* phead, DataType x);//查找目标节点

List.c

#include"List.h"
ListNode* ListBuyNode(DataType x)
{ListNode* node = (ListNode*)malloc(sizeof(ListNode));if (node == NULL){perror("malloc fail!");return NULL;}node->next = node;//值得注意的是在创造新节点的时候,双向链表和单链表不同的是,单链表的next要指向NULLnode->prev = node;//而双向链表需要前后指针,即prev和next都要指向自己,构成循环。node->data = x;return node;
}
void ListInit(ListNode** pphead)
{*pphead = ListBuyNode(0);
}
void ListPushBack(ListNode* phead,DataType x)
{assert(phead);//双向链表的哨兵位phead不可能为NULL,如果phead都是NULL了,那么这个链表就不是双向链表。ListNode* newnode = ListBuyNode(x);newnode->next = phead;newnode->prev = phead->prev;phead->prev-> next = newnode;phead->prev = newnode;
}
void ListPrint(ListNode* phead)
{ListNode* pcur = phead->next;while (pcur != phead){printf("%d->", pcur->data);pcur = pcur->next;}printf("\n");
}
void ListPushFront(ListNode* phead,DataType x)
{assert(phead);ListNode* newnode = ListBuyNode(x);newnode->next = phead->next;newnode->prev = phead;phead->next->prev = newnode;phead->next = newnode;
}
void ListPopBack(ListNode* phead)
{assert(phead && phead->next != phead);ListNode* del = phead->prev;del->prev->next = phead;phead->prev = del->prev;free(del);del = NULL;
}
void ListPopFront(ListNode* phead)
{assert(phead&&phead->next!=phead);ListNode* del = phead->next;phead->next = del->next;del->next->prev = phead;free(del);del = NULL;
}
ListNode* ListFind(ListNode* phead,DataType x)
{ListNode* pcur = phead->next;while (pcur != phead){if (pcur->data == x){return pcur;}pcur = pcur->next;}return NULL;
}
void ListInsert(ListNode* pos,DataType x)
{ListNode* newnode = ListBuyNode(x);newnode->prev = pos;newnode->next = pos->next;pos->prev = newnode;pos->next = newnode;
}
void ListErase(ListNode* pos)
{pos->prev->next = pos->next;pos->next->prev = pos->prev;free(pos);pos = NULL;
}
void ListDestroy(ListNode* phead)//值得注意的是因为这里我们传的的是一级指针,
//所以在这里phead的改变,不会影响实参plist的改变,因此在主函数中我们需要主动让plist=NULL。
//在这里我们也可以传二级指针ListNode** pphead,这样*pphead的改变就可以影响实参plist的改变了。
{ListNode* pcur = phead;ListNode* next = pcur->next;while (pcur != phead){free(pcur);pcur = next;next = next->next;}free(phead);
}

上面的代码中,个别函数有些地方的关键点我有注释,有兴趣的小伙伴可以看一下。

test.c

#include"List.h"
//void test1()//测试双向链表初始化和创造节点
//{
//	ListNode* plist = NULL;
//	ListInit(&plist);
//}
//void test2()//测试双向链表尾插节点以及双向链表的打印
//{
//	ListNode* plist = NULL;
//	ListInit(&plist);
//	ListPushBack(plist, 1);
//	ListPushBack(plist, 2);
//	ListPushBack(plist, 3);
//	ListPushBack(plist, 4);
//	ListPrint(plist);
//}
//void test3()//测试打印双向链表
//{
//	ListNode* plist = NULL;
//	ListInit(&plist);
//	ListPushFront(plist, 1);
//	ListPushFront(plist, 2);
//	ListPushFront(plist, 3);
//	ListPushFront(plist, 4);
//	ListPrint(plist);
//}
//void test4()//测试尾删
//{
//	ListNode* plist = NULL;
//	ListInit(&plist);
//	ListPushBack(plist, 1);
//	ListPushBack(plist, 2);
//	ListPushBack(plist, 3);
//	ListPushBack(plist, 4);
//	ListPopBack(plist);//删一次
//	ListPopBack(plist);//删两次
//	ListPopBack(plist);//三次
//	ListPopBack(plist);//四次,下一次再删除会报错
//	ListPrint(plist);
//}
//void test5()//测试头删
//{
//	ListNode* plist = NULL;
//	ListInit(&plist);
//	ListPushBack(plist, 1);
//	ListPushBack(plist, 2);
//	ListPushBack(plist, 3);
//	ListPushBack(plist, 4);
//	ListPrint(plist);
//	ListPopFront(plist);//删一次
//	ListPrint(plist);
//	ListPopFront(plist);//2
//	ListPrint(plist);
//	ListPopFront(plist);//3
//	ListPrint(plist);
//	ListPopFront(plist);//4,下一次再删除会报错
//	ListPrint(plist);
//}
//void test6()//测试查找目标数据节点、指定节点之后插入数据、删除指定节点
//{
//	ListNode* plist = NULL;
//	ListInit(&plist);
//	ListPushBack(plist, 1);
//	ListPushBack(plist, 2);
//	ListPushBack(plist, 3);
//	ListPushBack(plist, 4);
//	ListNode* find = ListFind(plist, 1);
//	if (find == NULL)
//		printf("没找到\n");
//	else
//		printf("找到了,地址是%p\n", find);
//	ListErase(find);
//	ListPrint(plist);
//}
void test7()//链表的销毁
{ListNode* plist = NULL;ListInit(&plist);ListPushBack(plist, 1);ListPushBack(plist, 2);ListPushBack(plist, 3);ListPushBack(plist, 4);ListPrint(plist);ListDestroy(plist);ListPrint(plist);plist = NULL;
}
int main()
{//test1();//测试双向链表初始化和创造节点//test2();//测试双向链表尾插节点以及双向链表的打印//test3();//测试打印双向链表//test4();//测试尾删//test5();//测试头删//test6();//测试查找目标数据节点、指定节点之后插入数据、删除指定节点test7();//链表的销毁return 0;
}

上面的代码,是我在测试函数功能是否正常所写的代码。小伙伴们看看就行。

三.结言

关于如何通过双向链表实现数据的增删查改就讲到这里了,帅哥美女们,我们下次再见,拜拜。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/685342.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C# WinForm —— 14 CheckedListBox 复选列表框介绍

1. 简介 类似 ListBox&#xff0c;提供项的列表&#xff0c;区别就是 CheckedListBox 每一个项前面有个复选框 2. 常用属性 属性解释(Name)控件ID&#xff0c;在代码里引用的时候会用到,一般以 ckl 开头BackColor背景颜色BoderStyle边框样式&#xff1a;无、FixedSingle、F…

Django 管理员登录安全 OTP双因素认证

目前安全双因素 最基本的&#xff0c;django管理员 默认直接登录的。 本项目环境:Django 2.0.13django-otp 0.9.3 1 安装pip3 install django-otp0.9.3 2 配置文件 vim api_statistics/settings.py INSTALLED_APPS里增加django_otp,django_otp.plugins.otp_totp,MIDDLEWARE…

如何内网穿透,远程访问内网设备

文章目录 0.前言1.准备工作2.内网穿透原理3.配置公网服务器的frp5.配置访问内网主机6.配置win10的远程桌面访问&#xff08;win11类似&#xff09;7.参考资料 0.前言 最近想研究一些新东西&#xff0c;公司的机器不敢乱搞&#xff0c;公司测试的服务器安装软件太多&#xff0c…

[GXYCTF 2019]Ping Ping Ping(内联执行)、[鹤城杯 2021]EasyP ($_SERVER)

目录 [GXYCTF 2019]Ping Ping Ping 内联执行 [鹤城杯 2021]EasyP [PHP_SELF]、$_SERVER[SCRIPT_NAME] 与 $_SERVER[REQUEST_URI] RCE命令注入可参考&#xff1a; RCE漏洞及其绕过——[SWPUCTF 2021 新生赛]easyrce、caidao、babyrce-CSDN博客 [GXYCTF 2019]Ping Ping Pin…

steam错误代码118?报错118?手把手教你应对Steam错误代码攻略

steam是由美国游戏开发公司Valve开发的一款数字发行、数字版权管理、多人游戏和社交平台。它最初是为Valve公司所开发的游戏而设计的&#xff0c;但现在已经发展成为游戏行业最大的数字发行平台之一。Steam平台提供了丰富的游戏资源&#xff0c;包括最新的独立游戏、大型多人在…

unity制作app(5)--发送数据给数据库

这个之前做过&#xff0c;先不做照片的。下一节再做带照片的。 第一步 收集数据 1.先做一个AppModel结构体&#xff0c;这个结构体需要单做的。 using System; using System.Collections.Generic; using System.Linq; using System.Text; //using Assets.Model; public clas…

记录minio的bug(Object name contains unsupported characters.)

场景是我将后端服务从121.xxx.xxx.xxx服务器上转移到了另一台服务器10.xxx.xxx.xxx 但图片都还在121.xxx.xxx.xxx服务器上&#xff0c;同样我10.xxx.xxx.xxx也安装了minio并且我的后端服务配置的minio地址也是10.xxx.xxx.xxx 此时有一个业务通过minio客户端获取图片&#xf…

【数据结构】单链表和双链表

文章目录 一、链表的概念及结构二、链表的分类三、无头单向非循环链表1.单链表创建2.尾插和头插3.尾删和头删4.打印5.查找6.插入7.删除8.销毁 四、带头双向循环链表1.双链表的创建2.初始化3.判断链表是否为空4.尾插和头插5.尾删和头删6.查找7.插入8.删除9.销毁 五、总结链表和顺…

量化交易T0策略:非凸T0算法

T0策略又称日内交易策略&#xff0c;它的持仓时间较短&#xff0c;基于对未来短期股价走势的判断&#xff0c;通过低位买入、高位卖出的方式来获得价差收益&#xff0c;并且买入卖出交易在日内完成。 分类 策略逻辑分类(融券T0和底仓T0) 融券T0在券商创立两融账号&#xff0c…

算法系列--BFS解决拓扑排序

&#x1f495;"请努力活下去"&#x1f495; 作者&#xff1a;Lvzi 文章主要内容&#xff1a;算法系列–算法系列–BFS解决拓扑排序 大家好,今天为大家带来的是算法系列--BFS解决拓扑排序 前言:什么是拓扑排序 拓扑排序–解决有顺序的排序问题(要做事情的先后顺序) …

云计算导论(2)---云计算基础

文章目录 1. 分布式计算2. 分布式计算系统架构3. 分布式计算关键技术4. 分布式计算性能优化方法5. 云计算的基本概念6. 云计算的关键技术 1. 分布式计算 1. 定义&#xff1a;分布式计算是一种计算方法&#xff0c;将一个大型任务拆分成多个小任务&#xff0c;并分配给多台计算机…

解决 Git拉取代码和本地代码丢失问题

git拉取代码&#xff0c;本地写的代码全部为空了&#xff0c;当时都蒙了&#xff0c;最后解决办法是找到对应文件的历史记录 举例&#xff1a;以本地的demo举例&#xff0c;不管是否有git或svn控制&#xff0c;都可以找到历史记录 解决办法&#xff1a; 1、对代码丢失的文件 …