数据结构————双向链表
内存泄漏:
内存泄漏(Memory Leak)是指程序中已动态分配的内存由于某种原因程序未释放或无法释放,导致系统内存的浪费,严重时会导致程序运行缓慢甚至崩溃。这种情况在长时间运行的程序或大型系统中尤为常见,因为它会随着时间的推移逐渐累积未释放的内存。
内存泄漏的原因
-
忘记释放内存:这是最常见的内存泄漏原因。程序员在申请内存后,由于疏忽或其他原因,忘记了在不再需要时释放它。
-
异常终止:如果程序在执行过程中因为异常(如错误、崩溃等)而终止,那么它可能无法执行正常的清理代码,导致内存泄漏。
-
缓存机制不当:有时为了优化性能,程序会缓存一些数据。如果缓存的数据量没有得到有效控制,或者缓存的清理策略不合理,就可能导致内存泄漏。
-
全局变量和静态变量:全局变量和静态变量的生命周期与程序相同,如果它们被用于存储动态分配的内存地址,并且这些内存在使用完毕后没有被释放,就会导致内存泄漏。
-
第三方库或API的使用:使用第三方库或API时,如果没有正确遵循其内存管理规则,也可能导致内存泄漏。
valgrind: 它主要用于内存调试、内存泄漏检测以及程序性能分析。
内存碎片:
内存碎片是指系统中所有不可用的空闲内存,这些碎片之所以不能被使用,是因为负责动态分配内存的分配算法使得这些空闲的内存无法使用。内存碎片可以分为外碎片和内碎片两种类型,下面分别进行详细介绍:
一、内存碎片的定义及分类
1. 外碎片
- 定义:外部碎片指的是还没有被分配出去(不属于任何进程),但由于太小了无法分配给申请内存空间的新进程的内存空闲区域。
- 产生原因:频繁的内存分配和释放、不同大小的内存分配、内存对齐问题等,都可能导致外碎片的产生。
2. 内碎片
- 定义:内部碎片就是已经被分配出去(能明确指出属于哪个进程)却不能被利用的内存空间。
- 产生原因:假设有一块连续空闲内存空间,当某个进程请求的内存大小与空闲内存块不完全匹配时,系统可能会分配一个稍大一点的内存块给该进程,从而产生内部碎片。
二、内存碎片的产生原因
- 频繁的内存分配和释放:这是导致内存碎片的主要原因之一。
- 不同大小的内存分配:当系统中分配的内存大小不一致时,也会导致碎片的产生。
- 内存对齐的问题:内存分配时如果没有进行对齐,也容易导致碎片。
双向有头链表:
双向有头链表是一种链表数据结构,与单向链表不同,双向链表中的每个节点都包含两个指针:一个指向下一个节点,另一个指向前一个节点。
创建链表:
Dlink_t *creat_doulink()
{Dlink_t *pdoulink = (Dlink_t *)malloc(sizeof(Dlink_t));if(NULL == pdoulink){perror("malloc fail");return NULL;}pdoulink->phead = NULL;pdoulink->clen = 0;pthread_mutex_init(&(pdoulink->mutex),NULL);return pdoulink;
}
遍历链表:
- 双向遍历:由于每个节点都包含前后两个指针,因此可以很方便地从前往后或从后往前遍历链表。
void doulink_for_each(DLink_t *pdoulink, int dir)
{if (is_empty_doulink(pdoulink))return;DLink_Node_t *p = pdoulink->phead;if (dir)//正向{while (p != NULL){printf("%d %s %d\n", p->data.id, p->data.name, p->data.score);p = p->pnext;}}else//反向{while (p->pnext != NULL){p = p->pnext;}while (p != NULL){printf("%d %s %d\n", p->data.id, p->data.name, p->data.score);p = p->ppre;}}printf("\n");}
头插:
int push_doulink_head(Dlink_t *pdoulink,DataType data)
{Dlink_Node_t *pnode =(Dlink_Node_t*) malloc(sizeof(Dlink_Node_t));if(NULL == pnode){perror("fail malloc");return -1;}pnode->data = data;pnode->ppre = NULL;pnode->pnext = NULL;if(is_empty_doulink(pdoulink)){pdoulink->phead = pnode;}else{pnode->pnext = pdoulink->phead;pdoulink->phead->ppre = pnode;pdoulink->phead = pnode;}pdoulink->clen++;return 0;
}
尾插:
int push_doulink_end(Dlink_t *pdoulink,DataType data)
{Dlink_Node_t *pnode = (Dlink_Node_t *)malloc(sizeof(Dlink_Node_t));if(pnode == NULL){perror("fail malloc");return -1;}pnode->data = data;pnode->ppre = NULL;pnode->pnext = NULL;if(is_empty_doulink(pdoulink)){pdoulink->phead = pnode;}else{Dlink_Node_t *p = pdoulink->phead;while(p->pnext != NULL){p = p->pnext;}p->pnext = pnode;pnode->ppre = p;}pdoulink->clen++;return 0;
}
头删:
int pop_doulink_head(Dlink_t *plink)
{if(is_empty_doulink(plink)){return 0;}Dlink_Node_t *p = plink->phead;plink->phead = p->pnext;p->ppre = NULL;free(p);plink->clen--;return 0;
}
尾删:
int pop_doulink_end(Dlink_t *plink)
{if(is_empty_doulink(plink))return 0;Dlink_Node_t *p = plink->phead;if(NULL == p->pnext){pop_doulink_head(plink);}while(p->pnext->pnext != NULL){p = p->pnext;}free(p->pnext);p->ppre = p;p->pnext = NULL;plink->clen--;
}
查找:
Dlink_Node_t *find_data(Dlink_t *plink,char *name)
{if(is_empty_doulink(plink))return 0;int i = 1;Dlink_Node_t *p = plink->phead;while(p != NULL){if(strcmp(name, p->data.name)==0){break;}p = p->pnext;if(p == NULL){return NULL;}}return p;
}
修改:
int change_data(Dlink_t *plink,char *name,int score)
{Dlink_Node_t *ptmp;ptmp = find_data(plink,name);Dlink_Node_t *p = plink->phead;while(p->pnext != NULL){if(p == ptmp){p->data.score = score;}p = p->pnext;}return 0;
}
销毁:
void destroy_doulink(Dlink_t *plink)
{while(!is_empty_doulink(plink)){ pop_doulink_head(plink);}free(plink);
}
双向链表的优势
双向链表相比于单向链表,具有以下几个显著的优势:
- 双向遍历能力:
- 双向链表中的每个节点都包含两个指针,一个指向前一个节点(ppre),另一个指向下一个节点(pnext)。这种结构使得双向链表可以从链表的任何一个节点开始,向前或向后遍历整个链表。相比之下,单向链表只能从头节点开始,逐个节点向后遍历,无法直接向前遍历。
- 高效的插入和删除操作:
- 在双向链表中插入或删除节点时,由于可以直接访问要插入或删除节点的前一个和/或后一个节点,因此可以更加高效地执行这些操作。例如,在双向链表中删除一个节点时,只需要修改该节点前后两个节点的指针,使其绕过被删除的节点即可,而不需要像单向链表那样可能需要遍历整个链表来找到要删除节点的前一个节点。
- 空间效率:
- 虽然双向链表中的每个节点需要额外的空间来存储指向前一个节点的指针,但从整体来看,它在内存使用上仍然具有较高的灵活性。链表可以在运行时动态地分配和释放内存,而不需要像数组那样预先分配固定大小的内存块。这种动态内存分配的特性使得双向链表在处理不确定大小的数据集时更加灵活。
单向链表的优势
-
内存占用较少:
单向链表的每个节点只需要存储一个指向下一个节点的指针(以及存储数据所需的空间)。而双向链表的每个节点除了存储数据外,还需要存储两个指针:一个指向前一个节点,另一个指向下一个节点。因此,在内存使用方面,单向链表更为节省。 -
实现更简单:
从实现的角度来看,单向链表的结构更为简单直接,因为它只涉及到一个方向的链接。这意味着在编写代码时,你可能需要处理更少的边界情况和指针操作,尤其是在实现一些基本的链表操作时(如插入和删除)。 -
特定的应用场景:
在某些特定的应用场景中,你可能只需要单向遍历链表,而不需要反向遍历。例如,当实现一个简单的栈(后进先出)或队列(先进先出)时,单向链表就足够了,因为它提供了所需的顺序访问功能,而无需额外的反向遍历能力。 -
兼容性:
在某些旧的或资源受限的系统中,双向链表的额外内存开销可能是一个问题。在这些情况下,单向链表由于其较小的内存占用而成为更合适的选择。 -
性能优势(在某些情况下):
虽然这一点可能并不总是成立,但在某些特定的情况下,单向链表可能具有更好的性能。例如,在遍历链表时,如果只需要从头到尾遍历一次,并且不需要反向遍历,那么单向链表可能会由于更简单的结构和更少的指针操作而表现出更好的缓存局部性,从而在某些情况下提高性能。
相关文章:
数据结构————双向链表
内存泄漏: 内存泄漏(Memory Leak)是指程序中已动态分配的内存由于某种原因程序未释放或无法释放,导致系统内存的浪费,严重时会导致程序运行缓慢甚至崩溃。这种情况在长时间运行的程序或大型系统中尤为常见,…...
55 - I. 二叉树的深度
comments: true difficulty: 简单 edit_url: https://github.com/doocs/leetcode/edit/main/lcof/%E9%9D%A2%E8%AF%95%E9%A2%9855%20-%20I.%20%E4%BA%8C%E5%8F%89%E6%A0%91%E7%9A%84%E6%B7%B1%E5%BA%A6/README.md 面试题 55 - I. 二叉树的深度 题目描述 输入一棵二叉树的根节点…...

Redis——初识Redis
初识Redis Redis认识Redis 分布式系统单机架构为什么要引入分布式理解负载均衡数据库的读写分离引入主从数据库 引入缓存数据库分库分表业务拆分——微服务常见概念了解 Redis背景介绍特性应用场景Redis不能做的事情Redis客户端redis客户端的多种形态 Redis 认识Redis 存储数…...

Xshell or Xftp提示“要继续使用此程序,您必须应用最新的更新或使用新版本”
Xshell提示“要继续使用此程序,您必须应用最新的更新或使用新版本”,笔者版本是xshell 6 方法一:更改系统时间 对于Windows 10用户,首先找到系统日期,右键点击并选择“调整时间/日期”。将日期设定为上一年。完成调整后&#x…...

table用position: sticky固定多层表头,滑动滚动条border边框透明解决方法
问题:我发现,这个上下滑动有内容经过就会出现如图的情况。 解决的方法:用outline(轮廓)替代border,以达到我们想要的样式。 outline主要是在元素边框的外围设置轮廓,outline不占据空间,绘制于…...

基于飞桨paddle2.6.1+cuda11.7+paddleRS开发版的目标提取-道路数据集训练和预测代码
基于飞桨paddle2.6.1cuda11.7paddleRS开发版的目标提取-道路数据集训练和预测代码 预测结果: 预测影像: (一)准备道路数据集 下载数据集地址: https://aistudio.baidu.com/datasetdetail/56961 mass_road.zip …...
数学建模笔记—— 整数规划和0-1规划
数学建模笔记—— 整数规划和0-1规划 整数规划和0-1规划1. 模型原理1.1 基本概念1.2 线性整数规划求解1.3 线性0-1规划的求解 2. 典型例题2.1 背包问题2.2 指派问题 3. matlab代码实现3.1 背包问题3.2 指派问题 整数规划和0-1规划 1. 模型原理 1.1 基本概念 在规划问题中&am…...

[001-03-007].第26节:分布式锁迭代3->优化基于setnx命令实现的分布式锁-防锁的误删
我的博客大纲 我的后端学习大纲 1、问题分析: 1.1.问题: 1.锁的超时释放,可能会释放其他服务器的锁 1.2.场景: 1.如果业务逻辑的执行时间是7s。执行流程如下 1.index1业务逻辑没执行完,3秒后锁被自动释放。2.index…...

【Unity踩坑】为什么有Rigidbody的物体运行时位置会变化
先上图,不知你有没有注意过这个现象呢? 一个物体加上了Rigidbody组件,当勾选上Use Gravity时,运行后,这个物体的位置的值会有变化。这是为什么呢? 刚体由物理系统处理,因此它会对重力、碰撞等做…...

NGINX开启HTTP3,给web应用提个速
环境说明 linuxdockernginx版本:1.27 HTTP3/QUIC介绍 HTTP3是由IETF于2022年发布的一个标准,文档地址为:https://datatracker.ietf.org/doc/html/rfc9114 如rfc9114所述,http3主要基于QUIC协议实现,在具备高性能的同时又兼备了…...
秋招季!别浮躁!
好久没写了,今天兴致来了,众所周知我一旦想说话,就来这里疯狂写。 最近,我去了一家国企的研究院,听着是不是贼高大上,呵——这玩意儿把我分配到三级机构,我一个学计算机的,它不把我…...

Java的时间复杂度和空间复杂度和常见排序
目录 一丶时间复杂度 二丶空间复杂度 三丶Java常见排序 1. 冒泡排序(Bubble Sort) 2.插入排序(Insertion Sort) 3.希尔排序(Shell Sort) 4.选择排序(Selection Sort) 5.堆排序&am…...

Qt 学习第十天:标准对话框 页面布局
系统标准对话框 错误对话框 //错误对话框connect(createPro, &QAction::triggered, this, []{//参数1 父亲 参数2 标题 参数3 对话框内显示文本内容 。。。QMessageBox::critical(this, "报错!", "没加头文件!");}); 【运行结果】 信息对话框 co…...
体育数据API纳米足球数据API:足球数据接口文档API示例⑩
纳米体育数据的数据接口通过JSON拉流方式获取200多个国家的体育赛事实时数据或历史数据的编程接口,无请求次数限制,可按需购买,接口稳定高效; 覆盖项目包括足球、篮球、网球、电子竞技、奥运等专题、数据内容。纳米数据API2.0版本…...

[数据集][目标检测]高铁受电弓检测数据集VOC+YOLO格式1245张2类别
数据集格式:Pascal VOC格式YOLO格式(不包含分割路径的txt文件,仅仅包含jpg图片以及对应的VOC格式xml文件和yolo格式txt文件) 图片数量(jpg文件个数):1245 标注数量(xml文件个数):1245 标注数量(txt文件个数):1245 标注…...
Vuex:深入理解所涉及的几个问题
你好,我是沐爸,欢迎点赞、收藏、评论和关注。 一、Vuex 是什么? Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。它采用集中式存储管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化。 二、Vu…...
vue原理分析(六)研究new Vue()
今天我们来分析使用new Vue() 之前研究时,只是说是在创建一个实例。并没有深入进行研究 在vue的源码中找下Vue的构造函数 function Vue(options) {if (!(this instanceof Vue)) {warn$2(Vue is a constructor and should be called with the new keyword);}this.…...

滑动窗口+动态规划
前言:分析这个题目的时候,就知道要这两个线段要分开,但是要保证得到最优解,那么我们在选取第二根线段的时候,要保证我们第一根线段是左边最优解 并且我们选的两根线段的右端点一定是我们的数组的点(贪心思…...

vscode配置django环境并创建django项目
1、创建文件夹 创建文件夹 并在vscode打开 终端输入命令 “ python -m venv env ” 查看目录结构 2、创建项目 在终端输入 django-admin startproject 文件名(这里以myshop为例) 3、创建应用 在myshop打开终端 在终端输入 django-admin startapp 应用名 这里以app1为例…...

WebGL系列教程四(绘制彩色三角形)
目录 1 前言2 varying变量介绍3 开始绘制3.1 声明顶点着色器3.2 声明片元着色器3.3 创建顶点和颜色的缓冲区3.4 指定变量从缓冲区获取值3.5 效果3.6 varying的内涵3.7 完整代码 4 总结 1 前言 上一篇中我们介绍了如何使用缓冲区来绘制三角形,这一篇我们来讲讲如何给…...
云原生核心技术 (7/12): K8s 核心概念白话解读(上):Pod 和 Deployment 究竟是什么?
大家好,欢迎来到《云原生核心技术》系列的第七篇! 在上一篇,我们成功地使用 Minikube 或 kind 在自己的电脑上搭建起了一个迷你但功能完备的 Kubernetes 集群。现在,我们就像一个拥有了一块崭新数字土地的农场主,是时…...

.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...

CMake基础:构建流程详解
目录 1.CMake构建过程的基本流程 2.CMake构建的具体步骤 2.1.创建构建目录 2.2.使用 CMake 生成构建文件 2.3.编译和构建 2.4.清理构建文件 2.5.重新配置和构建 3.跨平台构建示例 4.工具链与交叉编译 5.CMake构建后的项目结构解析 5.1.CMake构建后的目录结构 5.2.构…...

屋顶变身“发电站” ,中天合创屋面分布式光伏发电项目顺利并网!
5月28日,中天合创屋面分布式光伏发电项目顺利并网发电,该项目位于内蒙古自治区鄂尔多斯市乌审旗,项目利用中天合创聚乙烯、聚丙烯仓库屋面作为场地建设光伏电站,总装机容量为9.96MWp。 项目投运后,每年可节约标煤3670…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...
python报错No module named ‘tensorflow.keras‘
是由于不同版本的tensorflow下的keras所在的路径不同,结合所安装的tensorflow的目录结构修改from语句即可。 原语句: from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后: from tensorflow.python.keras.lay…...
go 里面的指针
指针 在 Go 中,指针(pointer)是一个变量的内存地址,就像 C 语言那样: a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10,通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...
CppCon 2015 学习:Time Programming Fundamentals
Civil Time 公历时间 特点: 共 6 个字段: Year(年)Month(月)Day(日)Hour(小时)Minute(分钟)Second(秒) 表示…...
用递归算法解锁「子集」问题 —— LeetCode 78题解析
文章目录 一、题目介绍二、递归思路详解:从决策树开始理解三、解法一:二叉决策树 DFS四、解法二:组合式回溯写法(推荐)五、解法对比 递归算法是编程中一种非常强大且常见的思想,它能够优雅地解决很多复杂的…...

ubuntu中安装conda的后遗症
缘由: 在编译rk3588的sdk时,遇到编译buildroot失败,提示如下: 提示缺失expect,但是实测相关工具是在的,如下显示: 然后查找借助各个ai工具,重新安装相关的工具,依然无解。 解决&am…...