当前位置: 首页 > news >正文

【hello C++】内存管理

目录

前言: 

1. C/C++内存分布

2. C语言动态内存管理方式

3. C++内存管理方式

3.1 new / delete 操作内置类型

3.2 new和delete操作自定义类型

4. operator new与operator delete函数

4.1 operator new与operator delete函数

5. new和delete的实现原理

5.1 内置类型

5.2 自定义类型

6. 定位new表达式(placement-new)

malloc/free和new/delete的区别



 C++🌷

前言: 

在Windows操作系统中,我们使用VS来编写C/C++代码,那编写好的代码到底是存在哪里呢?

 

其实,不论是我们编写好的代码,还是经过编译、链接、执行,生成的目标文件还是可执行

文件都是以文件的形式存在磁盘上的。

知道了整个代码文件存在哪里,那运行起来代码中的数据、指令代码存在哪里呢?

1. C/C++内存分布

在之前的学习中,我们知道C/C++程序内存大致划分为4个区域:

1.  栈:又叫堆栈--一般存储:非静态局部变量/函数参数/返回值等等,栈是向下增长的。
2.  堆:用于程序运行时动态内存分配,堆是可以上增长的。
3.  数据段:--一般存储:存储全局数据和静态数据。
4.  代码段:--一般存储:可执行的代码/只读常量。
知道了上述内容,我们先来看下面的一段代码和相关问题来练练手:
int globalVar = 1;
static int staticGlobalVar = 1;
void Test()
{static int staticVar = 1;int localVar = 1;int num1[10] = { 1, 2, 3, 4 };char char2[] = "abcd";const char* pChar3 = "abcd";int* ptr1 = (int*)malloc(sizeof(int) * 4);int* ptr2 = (int*)calloc(4, sizeof(int));int* ptr3 = (int*)realloc(ptr2, sizeof(int) * 4);free(ptr1);free(ptr3);
}
1. 选择题:选项 : A.栈  B.堆  C.数据段(静态区)  D.代码段(常量区)globalVar在哪里?____   staticGlobalVar在哪里?____staticVar在哪里?____   localVar在哪里?____num1 在哪里?____char2在哪里?____ * char2在哪里?___pChar3在哪里?____ * pChar3在哪里?____ptr1在哪里?____ * ptr1在哪里?____
2. 填空题:sizeof(num1) = ____;sizeof(char2) = ____;      strlen(char2) = ____;sizeof(pChar3) = ____;     strlen(pChar3) = ____;sizeof(ptr1) = ____;
【答案】1. C C C A  A        A A A D A B;        2. 40 5 4 4/8 4 4/8
有问题的老铁们可以评论指出,看到我会解答!

下面这张图大家也可以看一下:

在这里顺便回顾一个知识点:

3. sizeof 和 strlen 区别?

 【答】:sizeof是一个运算符,我们通常使用它来求字节数,使用方法为:sizeof()括号里面

为想要求的字节数的内容;

strlen为一个函数,我们通常使用它来求字符串的长度。

2. C语言动态内存管理方式

在C语言中我们通常使用malloc / calloc / realloc / free的方式来完成动态内存的开辟和释放。

在之前的博客中我有具体写到它们的使用及区别,大家可以看下:

C语言中的内存管理

3. C++内存管理方式

C++是兼容C语言的,C++也可以使用malloc / free等方式来完成动态内存的开辟和释放。

C++又是对C语言的一种优化,在C语言的基础上提出了自己的内存管理方式:

通过newdelete操作符进行动态内存管理

我们知道C++的数据类型分为内置类型 和 自定义类型,下面看一下它们的具体使用:

3.1 new / delete 操作内置类型

void Test()
{//动态申请一个int类型的空间int* p1 = new int;//动态申请一个int类型的空间并初始化为10int* p2 = new int(10);//动态申请3个int类型的空间int* p3 = new int[3];delete p1;	//释放delete p2;delete[] p3;
}

下面看下图解:

 这里其实new和delete 与 malloc和free完成的功能差不多,都是内存的开辟和释放工作;

 但是new和delete是不是比malloc和free方便很多呢?

但这里也有非常要注意的东西:

申请和释放单个元素的空间,使用newdelete操作符;

申请和释放连续的空间,使用 new[]和delete[];

注意:匹配起来使用。

补充:

其实在C++11中支持 new[] 用{}初始化;

但在C++98中,这是不支持的。

3.2 new和delete操作自定义类型

#define _CRT_SECURE_NO_WARNINGS 1#include <iostream>
using namespace std;class A
{
public:A(int a = 0):_a(a){cout << "A():" << this << endl;}~A(){cout << "~A():" << this << endl;}
private:int _a;
};int main()
{// new/delete 和 malloc/free最大区别是:// new/delete对于【自定义类型】除了开空间还会调用构造函数和析构函数A* p1 = (A*)malloc(sizeof(A));cout << endl << endl;A* p2 = new A(1);free(p1);cout << endl << endl;delete p2;// 内置类型是几乎是一样的int* p3 = (int*)malloc(sizeof(int));int* p4 = new int;free(p3);delete p4;cout << endl << endl;A* p5 = (A*)malloc(sizeof(A) * 10);A* p6 = new A[10];free(p5);delete[] p6;return 0;
}

new对于自定义类型自动调用它的构造;

delete对于自定义类型自动调用它的析构。 

 

 new和delete对于内置类型同malloc和free功能类似:

 总结:

在申请自定义类型的空间时, new 会调用构造函数, delete 会调用析构函数,而 malloc
free 不会
在申请内置数据类型的空间时,new/delete 和 malloc/free 完成的功能类似;

4. operator newoperator delete函数

4.1 operator newoperator delete函数

new和delete是用户进行动态内存申请和释放的操作符,operator new 和operator delete是
系统提供的全局函数,new在底层调用operator new全局函数来申请空间,delete在底层通过
operator delete全局函数来释放空间。

注意:

operator new只是完成了开辟空间的操作,

new:开辟空间+构造函数(自定义类型)构造; 

 这个链接很重要:一定要看!!!

operator new 和 new的关系

下面的内容了解即可:

/*
operator new:该函数实际通过malloc来申请空间,当malloc申请空间成功时直接返回;申请空间
失败,尝试执行空               间不足应对措施,如果改应对措施用户设置了,则继续申请,否
则抛异常。
*/
void* __CRTDECL operator new(size_t size) _THROW1(_STD bad_alloc)
{// try to allocate size bytesvoid* p;while ((p = malloc(size)) == 0)if (_callnewh(size) == 0){// report no memory// 如果申请内存失败了,这里会抛出bad_alloc 类型异常static const std::bad_alloc nomem;_RAISE(nomem);}return (p);
}
/*
operator delete: 该函数最终是通过free来释放空间的
*/
void operator delete(void* pUserData)
{_CrtMemBlockHeader* pHead;RTCCALLBACK(_RTC_Free_hook, (pUserData, 0));if (pUserData == NULL)return;_mlock(_HEAP_LOCK);  /* block other threads */__TRY/* get a pointer to memory block header */pHead = pHdr(pUserData);/* verify block type */_ASSERTE(_BLOCK_TYPE_IS_VALID(pHead->nBlockUse));_free_dbg(pUserData, pHead->nBlockUse);__FINALLY_munlock(_HEAP_LOCK);  /* release other threads */__END_TRY_FINALLYreturn;
}
/*
free的实现
*/
#define   free(p)               _free_dbg(p, _NORMAL_BLOCK)
通过上述两个全局函数的实现知道, operator new 实际也是通过 malloc 来申请空间 ,如果
malloc 申请空间成功就直接返回,否则执行用户提供的空间不足应对措施,如果用户提供该
措施就继续申请,否则就抛异常。operator delete 最终是通过 free 来释放空间的

5. newdelete的实现原理

5.1 内置类型

如果申请的是内置类型的空间, new malloc delete free 基本类似;
不同的地方是: new/delete申请和释放的是单个元素的空间;
new[] delete[] 申请的是连续空间;
而且 new 在申请空间失败时会抛异常,不用检查返回值,malloc 会返回 NULL需要检查

#define _CRT_SECURE_NO_WARNINGS 1#include <iostream>
using namespace std;int main()
{//malloc失败返回空,需要检查char* p1 = (char*)malloc(1024 * 1024 * 1024);if (p1 == NULL)perror("malloc");printf("%p\n", p1);//new失败抛出异常,不要检查返回值try{char* p2 = new char[1024*1024*1024];printf("%p\n", p2);}catch (const exception& e){cout << e.what() << endl;}return 0;
}

上述抛出及捕获异常的方法,大家需要记一下;

如果57行抛出异常,那么不用执行后续代码,直接跳到60行执行。

补充:

每个类可以去实现自己专属operator new  new这个类对象,他就会调自己实现这个operator new

// 每个类可以去实现自己专属operator new  new这个类对象,他就会调自己实现这个operator new// 实现一个类专属的operator new  -- 了解一下
struct ListNode
{int _val;ListNode* _next;// 内存池static allocator<ListNode> alloc;void* operator new(size_t n){cout << "operator new -> STL内存池allocator申请" << endl;void* obj = alloc.allocate(1);return obj;}void operator delete(void* ptr){cout << "operator delete -> STL内存池allocator申请" << endl;alloc.deallocate((ListNode*)ptr, 1);}struct ListNode(int val):_val(val), _next(nullptr){}
};// allocator以后会讲,现在先会用即可
allocator<ListNode> ListNode::alloc;int main()
{// 频繁申请ListNode. 想提高效率 -- 申请ListNode时,不去malloc,而是自己定制内存池ListNode* node1 = new ListNode(1);ListNode* node2 = new ListNode(2);ListNode* node3 = new ListNode(3);delete node1;//delete node2;delete node3;A* p1 = new A;return 0;
}

operator new是一个封装好的函数,完成new的整个功能,其实内部也是通过malloc申请空间

的,不过在此基础之上,完成了对malloc申请失败后抛出异常的处理,使得new申请失败后

会抛出异常。

在使用new时,调用全局的operator new 和 类专属的operator new的区别:

调用全局的:每次调用每次向堆区申请空间,效率不高;

调用类专属的:有一个内存池,每次申请时申请内存池里面的内存空间,内存池没有了,一

下向堆区申请多个空间存在内存池里,再从内存池申请空间,效率较高;

5.2 自定义类型

1. new 的原理
1. 调用 operator new 函数申请空间;
2. 在申请的空间上执行构造函数,完成对象的构造;
2. delete 的原理
1. 在空间上执行析构函数,完成对象中资源的清理工作;
2. 调用 operator delete 函数释放对象的空间
3. new T[N] 的原理
1. 调用 operator new[] 函数,在 operator new[] 中实际调用 operator new 函数完成 N 个对象空间
的申请;
2. 在申请的空间上执行 N 次构造函数;
4. delete[ ] 的原理
1. 在释放的对象空间上执行 N 次析构函数,完成 N 个对象中资源的清理;
2. 调用 operator delete[] 释放空间,实际在 operator delete[] 中调用 operator delete 来释放空间

6. 定位new表达式(placement-new)

定位new 表达式:是在 已分配的原始内存空间中调用构造函数初始化一个对象。
使用格式:
new (place_address) type 或者new (place_address) type(initializer-list)
//place_address必须是一个指针,initializer-list是类型的初始化列表
使用场景:

定位new表达式在实际中一般是配合内存池使用。
因为内存池分配出的内存没有初始化,
所以如果是自定义类型的对象,需要使用new的定义表达式进行显示调构造函数进行初始化。    
class A
{
public:A(int a = 0): _a(a){cout << "A():" << this << endl;}~A(){cout << "~A():" << this << endl;}
private:int _a;
};// 定位new/replacement new
int main()
{// p1现在指向的只不过是与A对象相同大小的一段空间,还不能算是一个对象,因为构造函数没有执行A* p1 = (A*)malloc(sizeof(A));// 注意:如果A类的构造函数有参数时,此处需要传参new(p1)A; p1->~A();free(p1);A* p2 = (A*)operator new(sizeof(A));new(p2)A(10);p2->~A();operator delete(p2);return 0;
}

总结:

对于自定义类型:

new:开辟空间+构造;

也就是说:

operator new 与 定位new 联合起来就是new的整个功能。

malloc/freenew/delete的区别

malloc/free new/delete 的共同点是:都是从堆上申请空间,并且需要用户手动释放。
不同的地方是:
1. malloc free 是函数, new delete 是操作符;
2. malloc 申请的空间不会初始化, new 可以初始化;
3. malloc 申请空间时,需要手动计算空间大小并传递, new 只需在其后跟上空间的类型即可,
如果是多个对象, [ ] 中指定对象个数即可;
4. malloc 的返回值为 void*, 在使用时必须强转, new 不需要,因为 new 后跟的是空间的类型;
5. malloc 申请空间失败时,返回的是 NULL ,因此使用时必须判空, new 不需要,但是 new
要捕获异常;
6. 申请自定义类型对象时, malloc/free 只会开辟空间,不会调用构造函数与析构函数,而
new在申请空间后会调用构造函数完成对象的初始化,delete 在释放空间前会调用析构函数完
成空间中资源的清理

坚持打卡!

😃

相关文章:

【hello C++】内存管理

目录 前言&#xff1a; 1. C/C内存分布 2. C语言动态内存管理方式 3. C内存管理方式 3.1 new / delete 操作内置类型 3.2 new和delete操作自定义类型 4. operator new与operator delete函数 4.1 operator new与operator delete函数 5. new和delete的实现原理 5.1 内置类型 5.2…...

AppArmor零知识学习十二、源码构建(9)

本文内容参考&#xff1a; AppArmor / apparmor GitLab 接前一篇文章&#xff1a;AppArmor零知识学习十一、源码构建&#xff08;8&#xff09; 在前一篇文章中完成了apparmor源码构建的第六步——Apache mod_apparmor的构建和安装&#xff0c;本文继续往下进行。 四、源码…...

Unity - 带耗时 begin ... end 的耗时统计的Log - TSLog

CSharp Code // jave.lin 2023/04/21 带 timespan 的日志 &#xff08;不帶 log hierarchy 结构要求&#xff0c;即&#xff1a; 不带 stack 要求&#xff09;using System; using System.Collections.Generic; using System.IO; using UnityEditor; using UnityEngine;public…...

Python 智能项目:1~5

原文&#xff1a;Intelligent Projects Using Python 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 本文来自【ApacheCN 深度学习 译文集】&#xff0c;采用译后编辑&#xff08;MTPE&#xff09;流程来尽可能提升效率。 不要担心自己的形象&#xff0c;只关心如何实…...

C++设计模式:面试题精选集

目录标题 引言&#xff08;Introduction&#xff09;面试的重要性设计模式概述面试题的选择标准 设计模式简介 面试题解析&#xff1a;创建型模式&#xff08;Creational Patterns Analysis&#xff09;面试题与解答代码实例应用场景分析 面试题解析&#xff1a;结构型模式&…...

蓝桥 卷“兔”来袭编程竞赛专场-10仿射加密 题解

赛题介绍 挑战介绍 仿射密码结合了移位密码和乘数密码的特点&#xff0c;是一种替换密码。它是利用加密函数一个字母对一个字母的加密。加密函数是 yaxb(mod m) &#xff0c;且 a,b∈Zm &#xff08;a、b 的值在 m 范围内&#xff09;&#xff0c;且 a、m 互质。 m 是字符集的…...

android so库导致的闪退及tombstone分析

android中有3种crash情况&#xff1a;未捕获的异常、ANR和闪退。未捕获的异常一般用crash文件就可以记录异常信息&#xff0c;而ANR无响应表现就是界面卡着无法响应用户操作&#xff0c;而闪退则是整个app瞬间退出&#xff0c;个人感觉对用户造成的体验最差。闪退一般是由于调用…...

图结构基本知识

图 1. 相关概念2. 图的表示方式3. 图的遍历3.1 深度优先遍历&#xff08;DFS&#xff09;3.2 广度优先遍历&#xff08;BFS&#xff09; 1. 相关概念 图G(V,E) &#xff1a;一种数据结构&#xff0c;可表示“多对多”关系&#xff0c;由顶点集V和边集E组成&#xff1b;顶点(ve…...

Hibernate 的多种查询方式

Hibernate 是一个开源的 ORM&#xff08;对象关系映射&#xff09;框架&#xff0c;它可以将 Java 对象映射到数据库表中&#xff0c;实现对象与关系数据库的映射。Hibernate 提供了多种查询方式&#xff0c;包括 OID 检索、对象导航检索、HQL 检索、QBC 检索和 SQL 检索。除此…...

FreeRTOS 任务调度及相关函数详解(一)

文章目录 一、任务调度器开启函数 vTaskStartScheduler()二、内核相关硬件初始化函数 xPortStartScheduler()三、启动第一个任务 prvStartFirstTask()四、中断服务函数 xPortPendSVHandler()五、空闲任务 一、任务调度器开启函数 vTaskStartScheduler() 这个函数的功能就是开启…...

飞桨paddlespeech语音唤醒推理C实现

上篇&#xff08;飞桨paddlespeech 语音唤醒初探&#xff09;初探了paddlespeech下的语音唤醒方案&#xff0c;通过调试也搞清楚了里面的细节。因为是python 下的&#xff0c;不能直接部署&#xff0c;要想在嵌入式上部署需要有C下的推理实现&#xff0c;于是我就在C下把这个方…...

04-Mysql常用操作

1. DDL 常见数据库操作 # 查询所有数据库 show databases; # 查询当前数据库 select databases();# 使用数据库 use 数据库名;# 创建数据库 create database [if not exits] 数据库名; # []代表可选可不选# 删除数据库 drop database [if exits] 数据库名; 常见表操作 创建…...

TensorFlow 2 和 Keras 高级深度学习:1~5

原文&#xff1a;Advanced Deep Learning with TensorFlow 2 and Keras 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 本文来自【ApacheCN 深度学习 译文集】&#xff0c;采用译后编辑&#xff08;MTPE&#xff09;流程来尽可能提升效率。 不要担心自己的形象&#x…...

UML类图

一、UML 1、什么是UML&#xff1f; UML——Unified modeling language UML(统一建模语言)&#xff0c;是一种用于软件系统分析和设计的语言工具&#xff0c;它用于帮助软件开发人员进行思考和记录思路的结果。UML本身是一套符号的规定&#xff0c;就像数学符号和化学符号一样&…...

【Python】【进阶篇】二十六、Python爬虫的Scrapy爬虫框架

目录 二十六、Python爬虫的Scrapy爬虫框架26.1 Scrapy下载安装26.2 创建Scrapy爬虫项目1) 创建第一个Scrapy爬虫项目 26.3 Scrapy爬虫工作流程26.4 settings配置文件 二十六、Python爬虫的Scrapy爬虫框架 Scrapy 是一个基于 Twisted 实现的异步处理爬虫框架&#xff0c;该框架…...

PyTorch 深度学习实用指南:6~8

原文&#xff1a;PyTorch Deep Learning Hands-On 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 本文来自【ApacheCN 深度学习 译文集】&#xff0c;采用译后编辑&#xff08;MTPE&#xff09;流程来尽可能提升效率。 不要担心自己的形象&#xff0c;只关心如何实现目…...

数据湖 Hudi 核心概念

文章目录 什么是 Hudi ?Hudi 是如何对数据进行管理的&#xff1f;Hudi 表结构Hudi 核心概念 什么是 Hudi ? Hudi 是一个用于处理大数据湖的开源框架。 大数据湖是指一个大规模的、中心化的数据存储库&#xff0c;其中包含各种类型的数据&#xff0c;如结构化数据、半结构化…...

爬虫请求头Content-Length的计算方法

重点&#xff1a;使用node.js 环境计算&#xff0c;同时要让计算的数据通过JSON.stringify从对象变成string。 1. Blob size var str 中国 new Blob([str]).size // 6 2、Buffer.byteLength # node > var str 中国 undefined > Buffer.byteLength(str, utf8) 6 原文…...

Open Inventor 2023.1 Crack

发行说明 Open Inventor 2023.1&#xff08;次要版本&#xff09; 文档于 2023 年 4 月发布。 此版本中包含的增强功能和新功能&#xff1a; Open Inventor 10 版本编号更改体积可视化 单一分辨率的体绘制着色器中与裁剪和 ROI 相关的新功能MeshVizXLM 在 C 中扩展的剪辑线提…...

【华为OD机试真题】查找树中元素(查找二叉树节点)(javaC++python)100%通过率

查找树中元素 知识点树BFSQ搜索广搜 时间限制:1s空间限制:256MB限定语言:不限 题目描述: 已知树形结构的所有节点信息,现要求根据输入坐标(x,y)找到该节点保存的内容 值;其中: x表示节点所在的层数,根节点位于第0层,根节点的子节点位于第1层,依次类推; y表示节…...

Swift 协议扩展精进之路:解决 CoreData 托管实体子类的类型不匹配问题(下)

概述 在 Swift 开发语言中&#xff0c;各位秃头小码农们可以充分利用语法本身所带来的便利去劈荆斩棘。我们还可以恣意利用泛型、协议关联类型和协议扩展来进一步简化和优化我们复杂的代码需求。 不过&#xff0c;在涉及到多个子类派生于基类进行多态模拟的场景下&#xff0c;…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略

本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装&#xff1b;只需暴露 19530&#xff08;gRPC&#xff09;与 9091&#xff08;HTTP/WebUI&#xff09;两个端口&#xff0c;即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序

一、开发准备 ​​环境搭建​​&#xff1a; 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 ​​项目创建​​&#xff1a; File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...

Auto-Coder使用GPT-4o完成:在用TabPFN这个模型构建一个预测未来3天涨跌的分类任务

通过akshare库&#xff0c;获取股票数据&#xff0c;并生成TabPFN这个模型 可以识别、处理的格式&#xff0c;写一个完整的预处理示例&#xff0c;并构建一个预测未来 3 天股价涨跌的分类任务 用TabPFN这个模型构建一个预测未来 3 天股价涨跌的分类任务&#xff0c;进行预测并输…...

什么?连接服务器也能可视化显示界面?:基于X11 Forwarding + CentOS + MobaXterm实战指南

文章目录 什么是X11?环境准备实战步骤1️⃣ 服务器端配置(CentOS)2️⃣ 客户端配置(MobaXterm)3️⃣ 验证X11 Forwarding4️⃣ 运行自定义GUI程序(Python示例)5️⃣ 成功效果![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/55aefaea8a9f477e86d065227851fe3d.pn…...

NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合

在汽车智能化的汹涌浪潮中&#xff0c;车辆不再仅仅是传统的交通工具&#xff0c;而是逐步演变为高度智能的移动终端。这一转变的核心支撑&#xff0c;来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒&#xff08;T-Box&#xff09;方案&#xff1a;NXP S32K146 与…...

云原生安全实战:API网关Kong的鉴权与限流详解

&#x1f525;「炎码工坊」技术弹药已装填&#xff01; 点击关注 → 解锁工业级干货【工具实测|项目避坑|源码燃烧指南】 一、基础概念 1. API网关&#xff08;API Gateway&#xff09; API网关是微服务架构中的核心组件&#xff0c;负责统一管理所有API的流量入口。它像一座…...

AI语音助手的Python实现

引言 语音助手(如小爱同学、Siri)通过语音识别、自然语言处理(NLP)和语音合成技术,为用户提供直观、高效的交互体验。随着人工智能的普及,Python开发者可以利用开源库和AI模型,快速构建自定义语音助手。本文由浅入深,详细介绍如何使用Python开发AI语音助手,涵盖基础功…...

​​企业大模型服务合规指南:深度解析备案与登记制度​​

伴随AI技术的爆炸式发展&#xff0c;尤其是大模型&#xff08;LLM&#xff09;在各行各业的深度应用和整合&#xff0c;企业利用AI技术提升效率、创新服务的步伐不断加快。无论是像DeepSeek这样的前沿技术提供者&#xff0c;还是积极拥抱AI转型的传统企业&#xff0c;在面向公众…...

动态规划-1035.不相交的线-力扣(LeetCode)

一、题目解析 光看题目要求和例图&#xff0c;感觉这题好麻烦&#xff0c;直线不能相交啊&#xff0c;每个数字只属于一条连线啊等等&#xff0c;但我们结合题目所给的信息和例图的内容&#xff0c;这不就是最长公共子序列吗&#xff1f;&#xff0c;我们把最长公共子序列连线起…...