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

使用动态链表存储数据时,不需要预先申请内存空间,而是在需要的时候才向内存申请。当需要添加新的元素时,可以使用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. SpringBoot 项目实战 | 瑞吉外卖 Day06

    该系列将记录一份完整的实战项目的完成过程,该篇属于第六天 案例来自B站黑马程序员Java项目实战<瑞吉外卖>,请结合课程资料阅读以下内容 该篇我们将完成以下内容: 用户地址簿相关功能 菜品 ...

  2. JSP常见错误以及解决方案

    原作者为 RioTian@cnblogs, 本作品采用 CC 4.0 BY 进行许可,转载请注明出处. 本节我们分析一下常见的 JSP 错误信息,并给出解决方案.这些错误在实际开发中会经常遇到,所以有 ...

  3. 【每日一题】36. 小AA的数列 (二进制DP)

    补题链接:Here 算法涉及:位运算,DP 这道题想了很久但实在没想什么巧妙的解法,暴力的代码就不放,这里引用Kur1su 的思路 异或问题优先考虑二进制位,对于这个问题,我们需要考虑偶数长度的区间, ...

  4. .NET CORE实战项目之CMS 部署篇 思维导图

    .NET Core实战项目之CMS 第十七章 CMS网站系统的部署 如何优雅的利用Windows服务来部署ASP.NET Core程序

  5. 每天学五分钟 Liunx 0001 | 存储篇:swap

    swap swap ,内存交换空间,它是磁盘上的一块空间,主要作用是为了防止物理内存不足. CPU 从内存中读取数据.当内存的空间不足时, CPU 难以读取到数据,导致程序无法正常工作.所以诞生了 s ...

  6. C++中不支持strdup(),使用_strdup()

    1.问题 C4996 'strdup': The POSIX name for this item is deprecated. Instead, use the ISO C and C++ conf ...

  7. [转帖]How to Resolve ORA-3136 Inbound Connection Timed Out

    https://logic.edchen.org/how-to-resolve-ora-3136-inbound-connection-timed-out/#:~:text=ORA-03136%3A% ...

  8. [转帖]Jmeter压力测试工具安装及使用教程

    https://www.cnblogs.com/monjeo/p/9330464.html 一.Jmeter下载 进入官网:http://jmeter.apache.org/ 1.第一步进入官网如下图 ...

  9. [转帖]/dev/null 2>&1详解

    https://www.diewufeiyang.com/post/1045.html shell中可能经常能看到:>/dev/null 2>&1 命令的结果可以通过%>的形 ...

  10. [转帖]Elasticsearch 的 30 个调优最佳实践!

    Elasticsearch 的 30 个调优最佳实践! https://zhuanlan.zhihu.com/p/406264041 ES 发布时带有的默认值,可为 es 的开箱即用带来很好的体验.全 ...