动态链表是一种常用的动态数据结构,可以在运行时动态地申请内存空间来存储数据,相比于静态数组和静态链表,更加灵活和高效。在动态链表中,数据元素被组织成一条链表,每个元素包含了指向下一个元素的指针,这样就可以通过指针将所有元素串联起来。

使用动态链表存储数据时,不需要预先申请内存空间,而是在需要的时候才向内存申请。当需要添加新的元素时,可以使用malloc函数动态地申请内存空间,然后将新的元素插入到链表中;当需要删除元素时,可以使用free函数释放元素占用的内存空间,然后将链表中的指针重新连接。

动态链表的优点在于可以随时插入或删除元素,而且不会浪费内存空间。但是它也有缺点,比如访问链表中的任何一个元素都需要遍历整个链表,时间复杂度较高,不适合随机访问操作。同时,动态链表还需要额外的指针来存储元素之间的关系,相比于静态数组来说,存储空间的开销会更大。

读者需自行创建头文件linklist.h并拷贝如下动态链表代码实现;

#include <stdio.h>
#include <string.h>
#include <stdlib.h> typedef void * LinkList;
typedef void(*FOREACH)(void *);
typedef int(*COMPARE)(void *, void *); // 链表节点数据类型
struct LinkNode
{
void *data;
struct LinkNode *next;
}; // 链表数据类型
struct LList
{
struct LinkNode header;
int size;
}; // 初始化链表
LinkList InitLinkList()
{
struct LList *list = malloc(sizeof(struct LList));
if (NULL == list)
return NULL; list->header.data = NULL;
list->header.next = NULL;
list->size = 0; return list;
} // 向节点插入数据
void InsertLinkList(LinkList list, int pos, void *data)
{
// 如果传入链表为空,或者插入数据为空则直接返回
if (NULL == list || NULL == data)
{
return;
} struct LList * mylist = (struct LList *)list; // 如果插入位置小于0或者是插入位置大于链表的结束
if (pos < 0 || pos > mylist->size)
{
pos = mylist->size;
} // 插入前先查找插入位置
struct LinkNode *pCurrent = &(mylist->header);
for (int i = 0; i < pos; ++i)
{
pCurrent = pCurrent->next;
} // 创建新节点,并初始化数据
struct LinkNode *newnode = malloc(sizeof(struct LinkNode));
newnode->data = data;
newnode->next = NULL; // 将创建的新节点插入到链表中
newnode->next = pCurrent->next;
pCurrent->next = newnode;
mylist->size++;
} // 遍历链表,传入myforeach回调函数,自定制输出
void ForeachLinkList(LinkList list, FOREACH myforeach)
{
if (NULL == list || NULL == myforeach)
{
return;
} // 获得链表头指针
struct LList * mylist = (struct LList *)list; // 指向下一个链表结构
struct LinkNode *pCurrent = mylist->header.next; while (pCurrent != NULL)
{
myforeach(pCurrent->data);
pCurrent = pCurrent->next;
}
} // 按照位置删除链表中的节点
void RemoveByPosLinkList(LinkList list, int pos)
{
if (NULL == list || NULL == pos)
{
return;
} struct LList *mylist = (struct LList *)list; // 插入位置小于0,或者插入位置大于最后一个元素
if (pos < 0 || pos > mylist->size - 1)
{
return;
} // 通过遍历指针寻找待插入元素的位置
struct LinkNode *pCurrent = &(mylist->header);
for (int i = 0; i < pos; ++i)
pCurrent = pCurrent->next; // 先保存待删除结点
struct LinkNode *pDel = pCurrent->next; // 重新建立待删除结点的前驱和后继结点关系
pCurrent->next = pDel->next; // 释放删除节点内存
free(pDel);
pDel = NULL;
mylist->size--;
} // 按照值删除链表中的节点
void RemoveByValLinkList(LinkList list, void *data, COMPARE compare)
{
if (NULL == list || NULL == data || NULL == compare)
{
return;
} struct LList *mylist = (struct LList *)list; // 辅助指针变量pPrev指向上一个元素pCurrent指向当前元素
struct LinkNode *pPrev = &(mylist->header);
struct LinkNode *pCurrent = pPrev->next; while (pCurrent != NULL)
{
if (compare(pCurrent->data, data))
{
// 找到了
pPrev->next = pCurrent->next; // 释放删除节点内存
free(pCurrent);
pCurrent = NULL;
mylist->size--;
break;
}
pPrev = pCurrent;
pCurrent = pCurrent->next;
}
} // 清空链表
void ClearLinkList(LinkList list)
{
if (NULL == list)
{
return;
} struct LList *mylist = (struct LList *)list; // 辅助指针变量
struct LinkNode *pCurrent = mylist->header.next; while (pCurrent != NULL)
{
// 先缓存下一个节点的地址
struct LinkNode *pNext = pCurrent->next;
// 释放当前结点内存
free(pCurrent);
pCurrent = pNext;
}
mylist->header.next = NULL;
mylist->size = 0;
} // 获取链表当前大小
int SizeLinkList(LinkList list)
{
if (NULL == list)
{
return -1;
} struct LList *mylist = (struct LList *)list;
return mylist->size;
} // 销毁链表
void DestroyLinkList(LinkList list)
{
if (NULL == list)
{
return;
} // 清空链表
ClearLinkList(list);
free(list);
list = NULL;
}

如下代码则是使用方法,通过调用链表操作库实现了对链表的创建、插入、删除、遍历等操作。在代码中定义了一个结构体Student,包含姓名和年龄两个字段。同时还定义了回调函数myPrintmyComapre,分别用于遍历链表时的数据输出和链表成员比较。

代码通过调用链表操作库实现对链表的操作,具体操作包括:

  • 初始化链表 InitLinkList
  • 插入链表元素 InsertLinkList
  • 删除链表元素 RemoveByPosLinkList 和 RemoveByValLinkList
  • 遍历链表元素 ForeachLinkList
  • 清空链表 ClearLinkList
  • 销毁链表 DestroyLinkList

其中,回调函数 myPrint 用于输出链表中的每个成员,回调函数 myCompare 用于比较链表中的成员是否相同。在代码中,还输出了链表的大小,即元素个数。

#include "LinkList.h"

struct Student
{
char name[64];
int age;
}; // 通过自定义回调函数实现输出个性化数据
void myPrint(void *data)
{
struct Student *stu = (struct Student *)data;
printf("姓名: %s 年龄: %d \n", stu->name, stu->age);
} // 链表对比方法
int myComapre(void *d1, void *d2)
{
struct Student *p1 = (struct Student *)d1;
struct Student *p2 = (struct Student *)d2;
if (strcmp(p1->name, p2->name) == 0 && p1->age == p2->age)
{
return 1;
}
return 0;
} int main(int argc, char *argv[])
{
// 创建链表首个节点
LinkList list = InitLinkList(); // 创建要插入的数据
struct Student stu1 = { "admin", 10 };
struct Student stu2 = { "guest", 20 };
struct Student stu3 = { "blib", 30 }; // 插入测试数据
InsertLinkList(list, 0, &stu1);
InsertLinkList(list, 1, &stu2);
InsertLinkList(list, 2, &stu3); printf("当前链表大小: %d \n", SizeLinkList(list)); // 输出链表元素
ForeachLinkList(list, myPrint); // 删除下标为2的元素
RemoveByPosLinkList(list, 2);
printf("当前链表大小: %d \n", SizeLinkList(list)); // 删除pDel的链表成员
struct Student pDel = { "admin", 10 };
RemoveByValLinkList(list, &pDel, myComapre);
ForeachLinkList(list, myPrint); // 清空链表
ClearLinkList(list); // 销毁链表
DestroyLinkList(list); system("pause");
return EXIT_SUCCESS;
}

7.2 C/C++ 实现动态链表的更多相关文章

  1. 使用C语言描述静态链表和动态链表

    静态链表和动态链表是线性表链式存储结构的两种不同的表示方式. 静态链表的初始长度一般是固定的,在做插入和删除操作时不需要移动元素,仅需修改指针,故仍具有链式存储结构的主要优点. 动态链表是相对于静态链 ...

  2. vc++基础班[28]---动态数组及动态链表的讲解

    C++中也有相应的动态数组.动态链表.映射表的模板类,就是STL中的:vector.list.map 他们属于C++标准中的一部分,对于程序的移植性来说也是不错的,但是在MFC编程中使用 CArray ...

  3. HDU 2095 find your present (2) 动态链表

    解题报告:输入一个n,后面紧跟着输入n个数,输入的这n个数中,除了有一个数的个数为奇数外,其它的数的个数都是偶数个,现在要你找出这个个数为奇数的这个数. 看起来好像很简单的样子,不过,这题的重点不在这 ...

  4. C++ 数据结构模板 队列 栈 动态链表 模板 Queue Stack List

    C++数据结构模板,可以实现基本功能,用法和stl差不多,比如Q.pop();Q.push(a);Q.front();...... (由于动态链表用的不多,若有错误望各位大神不吝赐教:) 队列: cl ...

  5. 01--数据结构——动态链表(C++)

    数据结构——动态链表(C++)   定义一个节点: [cpp] view plain copy   print? #include <iostream> using namespace s ...

  6. C++ -> 在使用动态链表和异质链表产生野指针的步骤

    C++ -> 在使用动态链表和异质链表产生野指针的步骤 使用异质链表产生野指针的情况,下面是修改书本的例子: ------------------------------------------ ...

  7. 数据结构——动态链表(C++)

    定义一个节点: [cpp] view plain copy   print? #include <iostream> using namespace std; typedef int T; ...

  8. C的动态链表建立

    运用到的函数为: 动态内存分配函数malloc()              比如:char *name=(char *)malloc(20);  相当与c++的new关键字 动态内存释放函数free ...

  9. C语言动态链表数据结构实现的学生信息项目

    注:此项目来源于吕鑫老师的教程 #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <conio.h> u ...

  10. C语言动态链表数据结构

    链表的操作增删改查 typedef int DATA; struct SNode { DATA data; SNode* pNext; }; SNode* g_head=NULL;//全局变量 //从 ...

随机推荐

  1. mysql--ERROR 2059 (HY000): Authentication plugin 'caching_sha2_password' cannot be loaded: /usr/lib64/mysql/plugin/caching_sha2_password.so: cannot open shared object file: No such file or directory

    问题背景: 1.在授权机器上连接mysql 8.0的数据库,账号.密码都没有问题,报错 2.使用 navicat工具连接8.0版本,报错 排查思路: 可能是创建用户没有指定插件使用了8.0自带的插件, ...

  2. Java ZIP文件解压

    Java ZIP文件解压 备忘笔记 代码: private byte[] unZip(byte[] data) { byte[] bArr = null; try { ByteArrayInputSt ...

  3. POJ1426: Find The Multiple

    题目: 给定一个正整数n,请编写一个程序来寻找n的一个非零的倍数m,这个m应当在十进制表示时每一位上只包含0或者1.你可以假定n不大于200且m不多于100位. 提示:本题采用Special Judg ...

  4. 最全!即学即会 Serverless Devs 基础入门(上)

    作者 | 刘宇(花名:江昱) 在上篇<即学即会 Serverless | 如何解决 Serverless 应用开发部署的难题>中,我们阐述了工具链的重要性,那么本文将带领各位快速实现 Se ...

  5. celery与django的结合以及定时任务配置

    一.conda创建新的开发环境 C:\Users\yc>conda create --name celery_django python=3.8 C:\Users\yc>conda inf ...

  6. Java求一个集合的所有子集

    转载请注明出处: 求一个集合的所有子集表示从一个集合当中,任取任意项或不取,所能得到的所有结果,比如有一个集合{a,b,c,d},那么{a,b}, {b, d}等都是它的子集,空集也是它的子集, 一个 ...

  7. Ubuntu Linux下的PDF阅读器推荐——Okular

    安装方法 在Ubuntu下直接使用sudo apt-get install okular即可,如果中间遇到依赖项的问题,可以通过运行sudo apt --fix-broken install来自动修复 ...

  8. 16-集电极开路门(OC门)

    集电极开路门(OC门) OC门 两个与非门,要实现非,一般来讲再与一下就可以. 能不能将输出端并在一起?普通的门电路永远不可能输出端并在一起,连在一起的. TTL与非门输出端连在一起 集电极断开之后连 ...

  9. 2023-SWPU NSS秋季招新赛(校外赛道)Misc—我要成为原神高手WP

    1.题目信息 我是神里绫华的狗!!! 2.解题方法 有个genshin.h文件夹,打开看看发现里面是一堆文件夹0 1A 1A0等等,而且每个文件夹里面都有文件,0 1A 1A0...看着很眼熟,我们用 ...

  10. 浏览器兼容 : IE10

    <script> /*@cc_on @*//*@ if (document.documentMode == 10) { // 只在 IE10 文档模式下运行,例如 IE10 浏览器或 IE ...