数据结构————双向链表
内存泄漏:
内存泄漏(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 前言 上一篇中我们介绍了如何使用缓冲区来绘制三角形,这一篇我们来讲讲如何给…...
别再死记硬背了!用Python/JavaScript/C++对比理解‘整型变布尔’的底层逻辑
别再死记硬背了!用Python/JavaScript/C对比理解‘整型变布尔’的底层逻辑 在编程语言的学习过程中,类型系统是最基础也最容易被忽视的部分。特别是当开发者从一门动态类型语言转向静态类型语言时,经常会遇到一些"反直觉"的类型转换…...
Fast-GitHub架构解析:基于Manifest V3的浏览器扩展网络加速方案
Fast-GitHub架构解析:基于Manifest V3的浏览器扩展网络加速方案 【免费下载链接】Fast-GitHub 国内Github下载很慢,用上了这个插件后,下载速度嗖嗖嗖的~! 项目地址: https://gitcode.com/gh_mirrors/fa/Fast-GitHub 技术架…...
告别NeRF的漫长等待:用3D Gaussian Splatting在Colab上5分钟跑通你的第一个3D场景
5分钟在Colab玩转3D高斯泼溅:零基础极速生成你的3D场景 当你想把几张随手拍的照片变成可自由旋转的3D场景时,传统方法可能需要数小时甚至更久的等待。现在,3D高斯泼溅(3D Gaussian Splatting)技术让这一切变得触手可及…...
如何用Lano Visualizer打造智能音频可视化桌面:从音乐爱好者到专业用户的完整指南
如何用Lano Visualizer打造智能音频可视化桌面:从音乐爱好者到专业用户的完整指南 【免费下载链接】Lano-Visualizer A simple but highly configurable visualizer with rounded bars. 项目地址: https://gitcode.com/gh_mirrors/la/Lano-Visualizer 你是否…...
Keil开发环境下的CANopen与DeviceNet协议实现指南
1. Keil开发工具对CANopen与DeviceNet协议的支持解析作为一名长期使用Keil工具链的嵌入式开发者,我经常遇到关于工业通信协议支持的咨询。最近在开发一个基于STM32的工业控制器时,就遇到了CANopen协议栈实现的问题。这里系统梳理下Keil开发环境对这两种主…...
相位恢复技术:XY-Hamiltonian优化框架与应用
1. 相位恢复问题的本质与挑战相位恢复是衍射成像领域长期存在的核心难题。当光波通过物体时,其振幅和相位信息都会发生变化。然而,传统的光学探测器(如CCD)只能记录光强(振幅平方),而丢失了关键…...
告别手动提交!用Bash脚本批量处理VASP+ShengBTE热输运计算的700+任务
计算材料学自动化革命:Bash脚本驱动的高通量热输运计算实践 在计算材料学领域,研究者常常需要处理数百甚至上千个相似的计算任务。以硅材料热输运性质计算为例,当使用VASP结合ShengBTE进行三阶力常数计算时,可能产生700多个独立的…...
国产高性能MCU如何破局?拆解先楫半导体RISC-V芯片的落地逻辑
1. 从展会到产线:拆解先楫半导体高性能MCU的落地逻辑前几天在深圳的Elexcon电子展上逛了一圈,最大的感触是,国产芯片的“高性能”这三个字,终于不再是PPT上的口号,而是能实实在在摸到、测到、甚至直接拿来设计产品的硬…...
别再点那个小箭头了!手把手教你用自定义按钮控制ElementUI表格展开行(Vue3 + Element Plus版)
用文字按钮重构Element Plus表格交互:让展开行操作更符合用户直觉 后台管理系统中最常见的交互痛点之一,就是默认的表格展开箭头设计。当用户面对密密麻麻的数据表格时,那个小小的三角形图标往往成为操作盲区。我曾参与过一个电商后台系统的用…...
从LMS到BLMS:自适应滤波的‘批处理’思想如何解决工程中的收敛难题?
从LMS到BLMS:批处理思想如何重塑自适应滤波的工程实践 在实时信号处理领域,工程师们常常面临一个经典困境:算法响应速度与系统稳定性能之间的微妙平衡。想象一下,当你正在调试一套语音降噪系统时,每次麦克风接收到一个…...
