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

使用动态链表存储数据时,不需要预先申请内存空间,而是在需要的时候才向内存申请。当需要添加新的元素时,可以使用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. 【库函数】Qt中Json的操作

    参考博客: https://blog.csdn.net/hp_cpp/article/details/80338116 从文件中读取json https://www.cnblogs.com/ybqjy ...

  2. 如何在CSDN上如何快速转载博客

    复制粘贴应该是最显而易见的方法,但是不仅会有丢失内容,而且格式也会丢失.要想达到更好的效果,可以从html源码入手. 1.在chrome浏览器中打开要转载的文章,右键选择检查(or使用F12) 2.在 ...

  3. Codeforces Round #700 (Div. 2) A ~ D1个人题解

    Codeforces Round #700 (Div. 2) 比赛链接: Click Here 1480A. Yet Another String Game 因为Alice是要追求小,Bob追求大值, ...

  4. 清洁低碳环保新能源,3D 光伏与光热发电站可视化

    前言 碳达峰.碳中和成为今年两会"热词",在国家政府工作报告中指出,扎实做好碳达峰.碳中和各项工作,制定 2030 年前碳排放达峰行动方案,优化产业结构和能源结构,实现低碳环保节能 ...

  5. vue2.x封装svg组件并使用

    https://blog.csdn.net/ChickenBro_/article/details/134027803

  6. sipp3.6 on centos7安装部署

    概述 在VOIP软交换的开发过程中,必然需要对软交换进行批量压测. SIP压测工具一般都是sipp,免费,开源,功能足够强大,配置灵活,优点多. 环境 centos7.9 cmake3.6 sipp ...

  7. freeswitch配置SBC的方案

    概述 freeswitch 是一款好用的开源软交换平台. 但是,fs不是专为SBC而开发的,所以需要做一些定制化的配置和开发. 本文主要介绍如何利用fs的基本功能配置一个简单的SBC方案,满足一般化需 ...

  8. [IDEA] - tomcat VM配置

    -Dfile.encoding=UTF-8

  9. Django应用中的静态文件处理

    在日常开发中,我们都是把Django的Debug模式打开,方便调试,在这个模式下,由Django内置的Web服务器提供静态文件服务,不过需要进行一些配置,才能正确访问. 配置settings # St ...

  10. [转帖]Shell三剑客之awk

    目录 awk简述 awk的工作过程 awk的工作原理 awk的基本格式及其内置变量 getline 文本内容匹配过滤打印 对字段进行处理打印 条件判断打印 awk的三元表达式与精准筛选用法 awk的精 ...