7.2 C/C++ 实现动态链表
动态链表是一种常用的动态数据结构,可以在运行时动态地申请内存空间来存储数据,相比于静态数组和静态链表,更加灵活和高效。在动态链表中,数据元素被组织成一条链表,每个元素包含了指向下一个元素的指针,这样就可以通过指针将所有元素串联起来。
使用动态链表存储数据时,不需要预先申请内存空间,而是在需要的时候才向内存申请。当需要添加新的元素时,可以使用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,包含姓名和年龄两个字段。同时还定义了回调函数myPrint和myComapre,分别用于遍历链表时的数据输出和链表成员比较。
代码通过调用链表操作库实现对链表的操作,具体操作包括:
- 初始化链表 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++ 实现动态链表的更多相关文章
- 使用C语言描述静态链表和动态链表
静态链表和动态链表是线性表链式存储结构的两种不同的表示方式. 静态链表的初始长度一般是固定的,在做插入和删除操作时不需要移动元素,仅需修改指针,故仍具有链式存储结构的主要优点. 动态链表是相对于静态链 ...
- vc++基础班[28]---动态数组及动态链表的讲解
C++中也有相应的动态数组.动态链表.映射表的模板类,就是STL中的:vector.list.map 他们属于C++标准中的一部分,对于程序的移植性来说也是不错的,但是在MFC编程中使用 CArray ...
- HDU 2095 find your present (2) 动态链表
解题报告:输入一个n,后面紧跟着输入n个数,输入的这n个数中,除了有一个数的个数为奇数外,其它的数的个数都是偶数个,现在要你找出这个个数为奇数的这个数. 看起来好像很简单的样子,不过,这题的重点不在这 ...
- C++ 数据结构模板 队列 栈 动态链表 模板 Queue Stack List
C++数据结构模板,可以实现基本功能,用法和stl差不多,比如Q.pop();Q.push(a);Q.front();...... (由于动态链表用的不多,若有错误望各位大神不吝赐教:) 队列: cl ...
- 01--数据结构——动态链表(C++)
数据结构——动态链表(C++) 定义一个节点: [cpp] view plain copy print? #include <iostream> using namespace s ...
- C++ -> 在使用动态链表和异质链表产生野指针的步骤
C++ -> 在使用动态链表和异质链表产生野指针的步骤 使用异质链表产生野指针的情况,下面是修改书本的例子: ------------------------------------------ ...
- 数据结构——动态链表(C++)
定义一个节点: [cpp] view plain copy print? #include <iostream> using namespace std; typedef int T; ...
- C的动态链表建立
运用到的函数为: 动态内存分配函数malloc() 比如:char *name=(char *)malloc(20); 相当与c++的new关键字 动态内存释放函数free ...
- C语言动态链表数据结构实现的学生信息项目
注:此项目来源于吕鑫老师的教程 #define _CRT_SECURE_NO_WARNINGS #include <iostream> #include <conio.h> u ...
- C语言动态链表数据结构
链表的操作增删改查 typedef int DATA; struct SNode { DATA data; SNode* pNext; }; SNode* g_head=NULL;//全局变量 //从 ...
随机推荐
- AtCoder Regular Contest 121 (AB题解)
补题链接:Here A - 2nd Greatest Distance 二维坐标图中有 \(n\) 个房子,现在规定两房子距离定义为: \(max(|x_i−x_j|,|y_i−y_j|)\) 求第二 ...
- 为什么 Serverless 能提升资源利用率?
木吴|阿里云智能高级技术专家 业务的负载往往不是一成不变的,而是随着时间呈现一定的上下波动.传统的应用构建方式一般是备足充分的资源以保障业务可用性,造成资源利用率不高的现象.随着容器技术的普及,应用可 ...
- 活动回顾|阿里云 Serverless 技术实战与创新上海站回放&PPT下载
5月27日"阿里云 Serverless 技术实战与创新"上海站圆满落幕.活动现场邀请了来自阿里云 一线技术专家,分享当前 Serverless 趋势和落地实践过程中的挑战和机遇: ...
- 通过部署流行 Web 框架掌握 Serverless 技术
大家好,我是霍大侠,本篇我们通过学习部署流行 Web 框架,如 Spring Boot,Express,Web IDE,让你掌握 Serverless 函数计算架构和技术,领略弹性并发.高可用的好处. ...
- vue学习笔记 四、定义组件(组件基本结构)
系列导航 vue学习笔记 一.环境搭建 vue学习笔记 二.环境搭建+项目创建 vue学习笔记 三.文件和目录结构 vue学习笔记 四.定义组件(组件基本结构) vue学习笔记 五.创建子组件实例 v ...
- vue实现word或pdf文档导出的功能
https://www.jianshu.com/p/73915ef6ac89 Vue - element-ui 中预览 word .exce.ppt以及pdf文件 https://blog.csdn. ...
- P1004-DP【绿】
这道题很有趣,暴搜的时间复杂度太过于凶残O(K*(2^n)^2)(K的意思是大常数),不过作为提高组T4,这道题数据范围太小了,感觉哪怕是离谱的暴搜也能过. 再加上一时半会没想好多项式时间复杂度的正解 ...
- C#设计模式05——抽象工厂的写法
C# 抽象工厂是一种创建型设计模式,它提供了一种封装一组相关或相互依赖对象创建的方法.该模式使得客户端能够创建与应用程序所使用的特定工厂无关的对象. 以下是C#抽象工厂5W1H的解释: 1. What ...
- java进阶(32)--Collections工具类
一.简介:Collection与Collections区别 1.Java.until.Collection是集合接口 2.Java.until.Collections是集合工具类,方便集合的操作 二. ...
- cs 保研经验贴 | 英语口试
很多夏令营都有英语面试环节.但这其实是有迹可循的,多说几遍就熟练了. 无论是笔试面试,还是联系导师 联系 hr,这种自我推销的事情,都会越做越熟练的.希望发表也是如此吧-(来自博零菜鸟的碎碎念-) 目 ...