数据结构————双向链表
内存泄漏:
内存泄漏(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 前言 上一篇中我们介绍了如何使用缓冲区来绘制三角形,这一篇我们来讲讲如何给…...
KubeSphere 容器平台高可用:环境搭建与可视化操作指南
Linux_k8s篇 欢迎来到Linux的世界,看笔记好好学多敲多打,每个人都是大神! 题目:KubeSphere 容器平台高可用:环境搭建与可视化操作指南 版本号: 1.0,0 作者: 老王要学习 日期: 2025.06.05 适用环境: Ubuntu22 文档说…...
智慧医疗能源事业线深度画像分析(上)
引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...
rknn优化教程(二)
文章目录 1. 前述2. 三方库的封装2.1 xrepo中的库2.2 xrepo之外的库2.2.1 opencv2.2.2 rknnrt2.2.3 spdlog 3. rknn_engine库 1. 前述 OK,开始写第二篇的内容了。这篇博客主要能写一下: 如何给一些三方库按照xmake方式进行封装,供调用如何按…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...
全球首个30米分辨率湿地数据集(2000—2022)
数据简介 今天我们分享的数据是全球30米分辨率湿地数据集,包含8种湿地亚类,该数据以0.5X0.5的瓦片存储,我们整理了所有属于中国的瓦片名称与其对应省份,方便大家研究使用。 该数据集作为全球首个30米分辨率、覆盖2000–2022年时间…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...
Linux --进程控制
本文从以下五个方面来初步认识进程控制: 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程,创建出来的进程就是子进程,原来的进程为父进程。…...
AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...
MySQL:分区的基本使用
目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区(Partitioning)是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分(分区)可以独立存储、管理和优化,…...
通过 Ansible 在 Windows 2022 上安装 IIS Web 服务器
拓扑结构 这是一个用于通过 Ansible 部署 IIS Web 服务器的实验室拓扑。 前提条件: 在被管理的节点上安装WinRm 准备一张自签名的证书 开放防火墙入站tcp 5985 5986端口 准备自签名证书 PS C:\Users\azureuser> $cert New-SelfSignedCertificate -DnsName &…...
