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

【数据结构】详解算法复杂度:时间复杂度和空间复杂度

🔥个人主页:艾莉丝努力练剑

❄专栏传送门:《C语言》、《数据结构与算法》

🍉学习方向:C/C++方向

⭐️人生格言:为天地立心,为生民立命,为往圣继绝学,为万世开太平


前言:从今天开始,我们就要开始一个新篇:【数据结构与算法】啦!有诗云:“开天辟地头一篇,道轮沧海尽书言嘛!我们之前用了34篇博客(不包括自我介绍哈)的篇幅把C语言的内容较为详细地(窃以为)给友友们介绍了一遍,我们紧接着就开始往下介绍数据结构与算法的知识点啦,数据结构与算法不分家,像算法复杂度、顺序表和链表、栈和队列、二叉树、排序这些知识点,看起来在内容上似乎没有如C语言一般多,实际上恰恰相反,这一部分内容非常丰富!大家一定要打起十二分的精神,认真学习这部分的内容!前面提到的这些知识点只是数据结构与算法内容的冰山一角(但不是说简单或者不重要,一点不简单!并且非常重要!),如同闯关游戏,由易到难,这些内容都只是初阶的数据结构知识,是往后一切大厦的根基,运输大队长(懂的友友们都知道哈)的文胆陈布雷(就是那位写出“如果战端一开,那就是地无分南北,人无分老幼,无论何人,皆有守土抗战之责任,皆应抱定牺牲一切之决心”)曾说过这么一句话喝博主这句话有异曲同工之妙:“大厦将构筑于沙丘之上”。正因如此,大家一定要认真学习数据结构与算法的内容,把基础打牢、夯实!

           在C语言部分我已经向大家说明了数据结构学习需要的C语言基础:指针(一级、二级)、结构体、动态内存管理、递归(函数栈帧的创建与销毁)。大家对这几个版块如果有些陌生了话,可以去C语言专栏重温一下,专栏链接博主在每篇文章的目录下面都会附上,大家点击传送就好。

           那么我们废话不多说,就从算法复杂度开始,正式进入【数据结构与算法】的内容学习!希望我的博客对友友们有所帮助!我们共同进步!


目录

正文

一、数据结构与算法的认识

(一)数据结构

(二)算法

(三)总结:认识数据结构与算法的重要性

二、如何衡量算法的好坏——引入复杂度

(一)衡量算法的好坏——算法效率

(二)复杂度的概念及其重要性

1、复杂度概念

2、复杂度的重要性

三、复杂度:时间复杂度和空间复杂度

(一)时间复杂度

1、时间复杂度的定义

2、T(N)函数式示例 ——引入大O渐进表示法 

3、示例加深巩固

(二)空间复杂度

1、空间复杂度的定义

2、示例加深巩固

四、复杂度(常见)的对比

五、算法题与复杂度

结尾



正文

一、数据结构与算法的认识

在还没有学习数据结构与算法的内容前,大抵会有友友好奇,为什么要把数据结构和算法放在一起讲,所以我们在正式学习内容之前,先来认识一下数据结构与算法的概念——

(一)数据结构

什么是数据结构?数据结构(英文名Data Structure)指的是计算机存储、组织数据(增删查改)的方式,即相互之间存在一种或多种特定关系地数组元素的集合。没有一种单一的数据结构能对所有用途都起效果,正因如此我们才要学习各种各样的数据结构,像哈希、树、图、线性表这些。

(二)算法

什么是算法呢?算法(英文名Algorithm)就是定义良好的计算过程,取一个或一组的值为输入,并产生一个或一组值作为输出,简而言之,算法就是一系列的计算步骤,用来将输入数据转化为输出结果。

比如咱们在C语言中学过的冒泡排序,就是无序的数组变成有序的数组。

注意啦,一道算法题可能存在多种思路。算法的具体实现就牵扯到本文的重点——复杂度了。

(三)总结:认识数据结构与算法的重要性

算法和数据结构是不分家的。

数据结构与算法是非常重要的一个版块,承上启下,我们现在学习初阶的数据结构不就是上承C语言,(在深入学习数据结构与算法之后——高阶数据结构)下启C++、操作系统、数据库等等相对来说学习起来十分棘手的版块。

从最近几年的趋势来看,各大厂商越来越重视对技术能力笔试,以前笔试中还分选择题、编程题,现在像深信服、美团、腾讯、科大讯飞等一票大厂的笔试都是清一色的编程题,由此可见,提升代码能力迫在眉睫,而要想写出好的代码,就要借助数据结构与算法的知识,才有可能把代码敲下来,笔试考察算法题也是可想而知,面试中一些大厂岗位实习的面试官比较看重程序员的技术水平(比如腾讯),会提问很多跟项目有关的问题,让你水也水不了,比如叫你介绍自己的项目、让你现场手撕几道算法题、考你为什么这个函数比malloc快啊诸如此类的问题。

总结:

数据结构与算法的学习没有捷径(应该说编程语言的学习本来就没有捷径可走,不然程序猿怎么还会掉头发呢?哈哈...这就是专业壁垒,也可以称为提高竞争力,二郎腿翘翘就能拿到十几个大厂offer,怎么可能呢,梦里才啥都有),还是告诉友友们两个学习的“秘诀”吧——

(1)死磕代码:敲一遍没感觉很正常,两遍、三遍、四遍...死磕,干就完了;

(2)画图!思考!——光思考是不够的,有的友友们盯着一道算法题光是看,玩君子动口不动手那套(开个玩笑),老话所谓“不动笔不读书”嘛,画图,我在介绍C语言的时候(尤其像一维数组、二维数组、指针这些)详解习题基本上都要画个图放在代码实现旁边,这样思路可能一下子就打开了,跟光思考不动笔的效果肯定不一样。

二、如何衡量算法的好坏——引入复杂度

(一)衡量算法的好坏——算法效率

在力扣(LeetCode)上面有这么一道题:轮转数组。力扣链接放这里,大家也可以试着去力扣做一做(力扣和博主以前推荐的牛客网一样,都是非常好的刷题网站,注册起来非常简单):轮转数组

我们的思路是:循环k次将数组元素向后移动一位

轮转一次数组元素就向后移动一位,并且保存最后一个位置的数据,我们这里用 i 表示,倒数第二位就是 i-1 ,为什么规定最后一位呢?因为如果规定第一个位置为 i ,在轮转过程中会被覆盖。

void rotate(int* nums, int numsSize, int k) 
{while (k--){int end = nums[numsSize - 1];for (int i = numsSize - 1; i > 0; i--){nums[i] = nums[i - 1];}nums[0] = end;}
}

执行和提交结果是有差异的, 执行通过了提交不一定能通过,

执行一下,通过了 ——

提交一下——

这是为啥?执行可以通过,提交却通过不了,我们应该如何衡量其好坏呢?

这就要引出一个概念了,即复杂度

算法的好与坏要从复杂度上去分析。

(二)复杂度的概念及其重要性

1、复杂度概念

算法在编写成可执行程序之后,我们运行需要耗费时间资源和空间(内存)资源,由此可见,衡量一个算法的好坏,一般是从时间和空间两个维度来衡量的,即时间复杂度空间复杂度

时间复杂度主要是衡量一个算法的运行快慢,而空间复杂度主要是衡量一个算法运行所需要的额外空间的大小。在计算机发展的早期,那些“大块头”计算机的存储容量很小,这时对空间复杂度才会比较在乎,近些年随着计算机行业的迅猛发展,技术日新月异,计算机的存储容量越来越大,已经达到了很高的程度,因此我们现在无需格外关注一个算法的空间复杂度了。

我们学习复杂度的重点就放在计算时间复杂度上。

2、复杂度的重要性

我们从两个角度谈谈其重要性:一个是从学习编程的角度,复杂度让程序员可以写出效率更高的代码,节省运算时间和空间;此外,从校招实习面试的考察也可以看出其作为一个考点的重要性,比如腾讯C++后台开发实习的一面曾考察“快排堆排归并时间复杂度,快排最坏的情况(上界),怎么推导的”这样一个问题。

可见,复杂度无论是作为一个学习的工具还是一个校招的考点都是非常重要的。

三、复杂度:时间复杂度和空间复杂度

(一)时间复杂度

1、时间复杂度的定义

在计算机科学中,算法的时间复杂度是用一个函数式T(N)来表示的,T(N)可以定量描述算法运行时间。既然时间复杂度是用来衡量程序的时间效率,那我们为什么不直接去计算程序的运行时间呢?其实有三个原因:

1、因为程序运行时间和编译环境、运行机器的配置都有关系,比如同一个算法程序,用一个老编译器编译和用一个新编译器编译,在相同的机器下运行时间不同;

2、同一个算法程序用一个低配置老机器运行和用一个高配置新机器运行,运行时间也不同;

3、并且这个时间只能在重新写好之后运行测试,做不到在写程序之前通过理论思想进行计算评估,故不能直接计算程序的运行时间。 

程序的执行时间  =  二进制指令运行时间  *  执行次数 

                           (假设时间是一定,是个常量)

 这个T(N)函数式计算了程序的执行次数。博主在C语言专栏中有一篇文章专门介绍了编译链接,算法程序被编译后生成二进制指令,程序执行,就是CPU执行这些编译好的指令,我们通过程序代码或者说理论思想计算出程序的执行次数的函数式T(N),假设每句指令执行时间基本一样(实际上是有差别的,只不过微乎其微可以忽略不计),执行次数和运行时间就等比例正相关,我们就可以脱离具体的编译运行环境,执行次数就可以代表程序时间效率的好坏优劣。

用T(N)这个函数式计算两个算法,出来的结果小的那个算法效率更优——

比如:A程序:T(N) = N^2,B程序:T(N) = N,B算法的效率就要优于A算法的效率。

2、T(N)函数式示例 ——引入大O渐进表示法 

要求:请计算一下Func1++count语句总共执行了多少次?

void Func1(int N)
{int count = 0;for (int i = 0; i < N; ++i){for (int j = 0; j < N; ++j){++count;}}for (int k = 0; k < 2 * N; ++k){++count;}int M = 10;while (M--){++count;}
}

为什么这么化?通过这几组对比我们发现,随着N的增大,(2N + 10)对结果的影响越来越小,已经可以忽略不计,对结果影响最大的一项是N^2,故取N^2。

实际计算时间复杂度时,计算的也不是程序的精确执行次数,精确的执行次数计算起来还是很麻烦的(不同的一句程序代码,编译出的指令条数都是不一样的),计算出精确执行次数的意义不大,因为我们计算时间复杂度只是相比较算法程序的增长量级,即N不断增大时T(N)的差别,从上图我们就可以看出,N变大的过程中低位阶、常数阶对结果的影响不大(尤其N=100、1000、...之后,影响非常小),因此我们只需要计算程序能代表增长量级的大概执行次数,复杂度的表示通常使用大O的渐进表示法

我们引入大O渐进表示法——

大O符号(Big O notation):是用于描述函数渐进行为的数字符号:

推导大O阶规则

1、时间复杂度函数式T(N)中,只保留最高阶项,去掉那些低阶项,因为当N不断变大时,低阶项对结果影响越来越小,当N无穷大时,就可以忽略不计了;

2、如果最高阶存在且系数不是1,则去除这个项目的常数系数,因为当N不断变大,这个系数对结果影响越来越小,当N无穷大,就可以忽略不计了;

3、T(N)中如果没有N相关的项目,只有常数项,用常数1取代所有加法常数

通过以上方法,可以得到Func1的时间复杂度为:O(N^2)。 

3、示例加深巩固

示例(1)

要求:计算Func2的时间复杂度——

void Func2(int N)
{int count = 0;for (int k = 0; k < 2 * N; ++k){++count;}int M = 10;while (M--){++count;}printf("%d\n", count);
}

for循环执行2N次,while循环执行10次,这里最高阶是N^1,根据大O阶规则(2),系数2随着N的增长对结果影响会越来越小,因此时间复杂度的结果就是O(N)。 

示例(2)

要求:计算Func3的时间复杂度——

void Func3(int N, int M)
{int count = 0;for (int k = 0; k < M; ++k){++count;}for (int k = 0; k < N; ++k){++count;}printf("%d\n", count);
}

注意:T(N)函数式中的这个N是指代变量的,不是说就是N。 

本题的结果其实可以进一步推理,过程已经画在图中了。

示例(3)

要求:计算Func4的时间复杂度——

void Func4(int N)
{int count = 0;for (int k = 0; k < 100; ++k){++count;}printf("%d\n", count);
}

执行次数只有常数项 ,没有N相关的项目,都用1取代所有的常数项,可能有朋友会问,如果这里这个常数项超级大,根据大O规则用1取代,不会对结果有影响吗?你的担忧没有问题,CPU计算100次和CPU计算100000000000000次时间还是有很大区别的,但是(西卡西,聪明的友友们都知道,但是后面才是重点),我在上图中画了一个坐标轴,只有常数项相当于一条直线,我们时间复杂度不是用来计算运行时间的,它是一个函数式,既然是函数式,它就是用来计算趋势的,所以如上图,随着N的变化,我们的时间没有任何变化,所以用1取代所有常数项没有问题。

示例(4)

要求:计算strchr的时间复杂度——

const char* strchr(const char* str, int character)
{const char* p_begin = s;while (*p_begin != character){if (*p_begin == '\0')return NULL;p_begin++;}return p_begin;
}

根据示例(4)我们可以总结一下:

 我们发现,一些算法的时间复杂度存在最好、最坏、平均三种情况——

最坏情况:任意输入规模的最大运行次数(上界)

最好情况:任意输入规模的最小运行次数(下界)

平均情况:任意输入规模的期望运行次数

大O的渐进表示法在实际中一般情况下关注的是算法的上界,也就是最坏运行情况。

示例(5)

要求:计算BubbleSort的时间复杂度——

void BubbleSort(int* a, int n)
{assert(a);for (size_t end = n; end > 0; --end){int exchange = 0;for (size_t i = 1; i < end; ++i){if (a[i - 1] > a[i]){Swap(&a[i - 1], &a[i]);exchange = 1;}}if (exchange == 0)break;}
}

示例(6)

要求:计算Func5的时间复杂度——

void func5(int n)
{int cnt = 1;while (cnt < n){cnt *= 2;}
}

注意对数的三种表示:

当n接近无穷大的时候,底数的大小对结果影响不大(换底公式),所以不管底数多大都可以省略不写,即写作log n,博主建议使用log n。

示例(7)

要求:计算阶乘递归Fac的时间复杂度——

long long Fac(size_t N)
{if (0 == N)return 1;return Fac(N - 1) * N;
}

(二)空间复杂度

空间复杂度也是一个数学表达式,是对一个算法在运行过程中因为算法的需要(比如在C语言动态内存管理中的malloc、calloc、realloc)需要额外临时开辟的空间

即函数体内因实现算法而要额外开辟的空间

void func1(int n)
{//函数体内因实现算法而需要额外开辟的空间
}
1、空间复杂度的定义

空间复杂度不是说程序占用多少字节的空间,正常情况下每个对象大小差异都不会很大,因此我们计算空间复杂度算的是变量的个数。

空间复杂度计算规则基本上和时间复杂度差不多,也是用大O渐进表示法来计算的。

注意:函数运行时所需要的栈空间(存储空间、局部变量、一些寄存器信息等)在编译期间已经确定好了,所以空间复杂度主要通过函数在运行时显式申请的额外空间来确定

2、示例加深巩固

空间复杂度相比时间复杂度,因为计算规则差不多,只不过变成计算变量的个数,例子就少举些:

例(1)

问:计算BubbleSort的空间复杂度?

void BubbleSort(int* a, int n)
{assert(a);for (size_t end = n; end > 0; --end){int exchange = 0;for (size_t i = 1; i < end; ++i){if (a[i - 1] > a[i]){Swap(&a[i - 1], &a[i]);exchange = 1;}}if (exchange == 0)break;}
}

 我们看下面的题,只不过这次我们算的是空间复杂度:

下面划出来的就是这个算法的三个变量——

BubbleSort额外申请的空间有exchange等有限个局部变量使用了常数个额外空间。 

我们知道,如果只有常数项,用常数1取代所有加法常数,这里直接算出来,是O(1)。

例(2)

计算阶乘递归Fac的空间复杂度?

long long Fac(size_t N)
{if (N == 0)return 1;return Fac(N - 1) * N;
}

 递归空间复杂度 = 递归次数 * 单次递归的空间复杂度

                                     N                          1

                  

四、复杂度(常见)的对比

下图是各个函数随n的增长其函数值的变化情况:

如下图,我们观察到:当n值增大时,时间复杂度的变化也越来越快,我们希望时间复杂度变化的慢一点,我们通过优化复杂度来优化代码。

右侧的时间复杂度从上往下变化程度呈增大趋势,O(n^2)开始变化就非常夸张了。 

下图是对比图: 

下面是各种各样的排序算法的复杂度表格:

五、算法题与复杂度

回到刚才那道力扣题——旋转数组,我们刚刚的思路是循环k次将数组元素向后移动一位,我们写出来的代码运行成功了,但是提交完告诉我们“超出时间限制”,学会了大O渐进表示法,我们可以计算出用这个思路写出来的算法的时间复杂度是:O(n^2),结合复杂度的表,我们可以发现O(n^2)那条线非常陡,O(n^2)是个不太好的复杂度,我们希望复杂度小一点。我们最开始的这个思路就是因为时间复杂度太高了,导致程序最终报错,超出了时间限制。

在讨论有没有其他思路之前,我们先看看思路(1)写出的算法的时间复杂度和空间复杂度是怎么算的,刚才我们说它的时间复杂度是O(n^2),怎么算出来的呢?我们来看看——

k和numsSize的范围都是到10^5,嵌套循环 k * numsSize即n*n,所以时间复杂度是O(n*2),空间复杂度是O(1),只有一个变量。

家人们,既然本质是让时间复杂度越小越好,有没有办法把时间复杂度的“价格”打下来!有的兄弟有的,我们可以把时间复杂度变成O(N),但是这种玩法会增加变量数,也就是说空间复杂度会增大,换言之,这是一种“空间换时间”的平衡之法,有点像端水大师哈。我们怎么做呢?

第二种思路,我们可以创建一个临时变量tmp,tmp大小和原数组一致,把原数组后k个数据依次放入tmp,再把剩下的数据依次放到tmp数组中。

//思路(2)
void rotate(int* nums, int numsSize, int k)
{//创建一个新数组tmpint tmp[numsSize];//遍历原数组,将数据轮转后放到tmp数组中for (int i = 0; i < numsSize; i++){tmp[(i + k) % numsSize] = nums[i];}//将tmp中的数据导入到nums数组for (int i = 0; i < numsSize; i++){nums[i] = tmp[i];}
}

 把图像画出来(学习数据结构,一个是死磕代码,一个就是画图+思考):

除余的好处在于不需要重复地循环就可以改数组。 

这个思路的结果就是“空间换时间” ,虽然时间复杂度的确如愿以偿地降低了,但是空间复杂度增加了,虽然代码已经能圆满完成任务,但我们可不可以加大难度——不用“空间换时间”,也就是说:时间复杂度还是减小到了O(n),但空间复杂度依旧让它为O(1),怎么做呢?

第三种思路,我们在C语言讲过逆置,我们这里三次逆置是不是也可以实现轮转数组?

//思路(3)
//逆置
void my_reverse(int* nums, int left, int right)
{while (left < right){//交换int tmp = nums[left];nums[left] = nums[right];nums[right] = tmp;left++;right--;}
}//numsSize = 1 k=2
void rotate(int* nums, int numsSize, int k)
{k = k % numsSize;//前n-k数据逆置my_reverse(nums, 0, numsSize - k - 1);//后k个数据逆置my_reverse(nums, numsSize - k, numsSize - 1);//整体逆置my_reverse(nums, 0, numsSize - 1);
}

画图+思考:可以收获1+1>2的效果—— 

这样一来,不仅时间复杂度的“价格”打下来了,空间复杂度也没有增大,皆大欢喜。

轮转数组这道题有这么多思路,正体现了那句话“算法思路有很多种”。


结尾

结语:

本篇文章内容到这里就结束了,本文主要介绍了复杂度的概念,复杂度又分为时间复杂度和空间复杂度,博主相信通过本文列举的一些经典的示例和复杂度算法题的讲解,大家对复杂度已经有了更深刻的认识,可能有朋友会问:“博主,复杂度的内容光掌握这些够用吗?”大家不用担心——像博主讲的时间复杂度的内容基本上覆盖未来笔试、面试有关时间复杂度的内容考察了,只要大家掌握博主讲的这些就可以“一招鲜吃遍天”了——当然开个玩笑哈。博主的意思是大家不需要额外地再去书上啊、什么课程上挖掘了(当然对复杂度特别感兴趣的就另说了),没必要,该掌握的本文都已经覆盖到了。的的确确,像算时间复杂度不同算法的复杂度可能不一样,也有特别复杂的,但是不用担心,把本文讲的那些内容掌握就OK啦! 

本期内容需要回顾的C语言知识放在下面了(指针博主写了6篇,列出来有水字数嫌疑了,就只放指针第六篇的网址,博主在指针(六)把指针部分的前五篇的网址都放在【往期回顾】了,点击【传送门】就可以去看了),大家如果对前面部分的知识点印象不深,可以去看看:

【动态内存管理】深入详解:malloc和free、calloc和realloc、常见的动态内存的错误、柔性数组、总结C/C++中程序内存区域划分

 【自定义类型:结构体】:类型声明、结构体变量的创建与初始化、内存对齐、传参、位段

C语言指针深入详解(六):sizeof和strlen的对比,【题解】数组和指针笔试题解析、指针运算笔试题解析

【深入详解】函数栈帧的创建与销毁:寄存器、压栈、出栈、调用、回收空间 

相关文章:

【数据结构】详解算法复杂度:时间复杂度和空间复杂度

&#x1f525;个人主页&#xff1a;艾莉丝努力练剑 ❄专栏传送门&#xff1a;《C语言》、《数据结构与算法》 &#x1f349;学习方向&#xff1a;C/C方向 ⭐️人生格言&#xff1a;为天地立心&#xff0c;为生民立命&#xff0c;为往圣继绝学&#xff0c;为万世开太平 前言&…...

Rest-Assured API 测试:基于 Java 和 TestNG 的接口自动化测试

1. 右键点击项目的文件夹&#xff0c;选择 New > File。 2. 输入文件名&#xff0c;例如 notes.md&#xff0c;然后点击 OK。 3. 选择项目类型 在左侧的 Generators 部分&#xff0c;选择 Maven Archetype&#xff0c;这将为你生成一个基于 Maven 的项目。 4. 配置项目基…...

多模型协同:基于 SAM 分割 + YOLO 检测 + ResNet 分类的工业开关状态实时监控方案

一、技术优势与适配性分析 1. 任务分工的合理性 YOLO&#xff08;目标检测&#xff09; 核心价值&#xff1a;快速定位工业开关在图像中的位置&#xff08;边界框&#xff09;&#xff0c;为后续分割和分类提供ROI&#xff08;感兴趣区域&#xff09;。工业场景适配性&#xf…...

【分销系统商城】

分销商城系统是一种结合电商与社交裂变的多层级分销管理平台&#xff0c;通过佣金激励用户成为分销商&#xff0c;实现低成本快速拓客和销量增长。以下是其核心要点解析&#xff1a; &#x1f6cd;️ ​​一、系统定义与核心价值​​ ​​基本概念​​ ​​核心模式​​&#…...

LangChainGo入门指南:Go语言实现与OpenAI/Qwen模型集成实战

目录 1、什么是langchainGo2、langchainGo的官方地址3、LangChainGo with OpenAI3-1、前置准备3-2、安装依赖库3-3、新建模型客户端3-4、使用模型进行对话 4、总结 1、什么是langchainGo langchaingo是langchain的go语言实现版本 2、langchainGo的官方地址 官网&#xff1a;…...

5.1 HarmonyOS NEXT系统级性能调优:内核调度、I/O优化与多线程管理实战

HarmonyOS NEXT系统级性能调优&#xff1a;内核调度、I/O优化与多线程管理实战 在HarmonyOS NEXT的全场景生态中&#xff0c;系统级性能调优是构建流畅、高效应用的关键。通过内核调度精细化控制、存储与网络I/O深度优化&#xff0c;以及多线程资源智能管理&#xff0c;开发者…...

react public/index.html文件使用env里面的变量

env文件 ENVdevelopment NODE_ENVdevelopment REACT_APP_URL#{REACT_APP_URL}# REACT_APP_CLIENTID#{REACT_APP_CLIENTID}# REACT_APP_TENANTID#{REACT_APP_TENANTID}# REACT_APP_REDIRECTURL#{REACT_APP_REDIRECTURL}# REACT_APP_DOMAIN_SCRIPT#{REACT_APP_DOMAIN_SCRIPT}#pu…...

chili3d 笔记17 c++ 编译hlr 带隐藏线工程图

这个要注册不然emscripten编译不起来 --------------- 行不通 ---------------- 结构体 using LineSegment std::pair<gp_Pnt, gp_Pnt>;using LineSegmentList std::vector<LineSegment>; EMSCRIPTEN_BINDINGS(Shape_Projection) {value_object<LineSegment&g…...

创建一个纯直线组成的字体库

纯直线组成的字体&#xff0c;一个“却”由五组坐标点组成&#xff0c;存储5个点共占21字节&#xff0c;使用简单&#xff0c;只要画直线即可&#xff0c; “微软雅黑”&#xff0c;2个轮廓&#xff0c;55坐标点&#xff0c;使用复杂&#xff0c;还填充。 自创直线字体 “微软…...

接口不是json的内容能用Jsonpath获取吗,如果不能,我们选用什么方法处理呢?

JsonPath 是一种专门用于查询和提取 JSON 数据的查询语言&#xff08;类似 XPath 用于 XML&#xff09;。以下是详细解答&#xff1a; ​JsonPath 的应用场景​ ​API 响应处理​&#xff1a;从 REST API 返回的 JSON 数据中提取特定字段。​配置文件解析​&#xff1a;读取 J…...

使用 Docker Compose 从零部署 TeamCity + PostgreSQL(详细新手教程)

JetBrains TeamCity 是一款专业的持续集成&#xff08;CI&#xff09;服务器工具&#xff0c;支持各种编程语言和构建流程。本文将一步一步带你用 Docker 和 Docker Compose 快速部署 TeamCity&#xff0c;搭配 PostgreSQL 数据库&#xff0c;并确保 所有操作新手可跟着做。 一…...

Go 语言实现高性能 EventBus 事件总线系统(含网络通信、微服务、并发异步实战)

前言 在现代微服务与事件驱动架构&#xff08;EDA&#xff09;中&#xff0c;事件总线&#xff08;EventBus&#xff09; 是实现模块解耦与系统异步处理的关键机制。 本文将以 Go 语言为基础&#xff0c;从零构建一个高性能、可扩展的事件总线系统&#xff0c;深入讲解&#…...

Linux进程(中)

目录 进程等待 为什么有进程等待 什么是进程等待 怎么做到进程等待 wait waitpid 进程等待 为什么有进程等待 僵尸进程无法杀死&#xff0c;需要进程等待来消灭他&#xff0c;进而解决内存泄漏问题--必须解决的 我们要通过进程等待&#xff0c;获得子进程退出情况--知…...

【计算机组成原理】计算机硬件的基本组成、详细结构、工作原理

引言 计算机如同现代科技的“大脑”&#xff0c;其硬件结构的设计逻辑承载着信息处理的核心奥秘。从早期程序员手动输入指令的低效操作&#xff0c;到冯诺依曼提出“存储程序”概念引发的革命性突破&#xff0c;计算机硬件经历了从机械操控到自动化逻辑的蜕变。本文将深入拆解…...

npm error Cannot read properties of null (reading ‘matches‘)

当在使用 npm 命令时遇到 Cannot read properties of null (reading matches) 错误&#xff0c;这通常表示代码尝试访问一个 null 对象的 matches 属性。以下是综合多个来源的解决策略&#xff0c;按优先级排列&#xff1a; 一、核心解决方法 1. 清理缓存与重新安装依赖&…...

MVC分层架构模式深入剖析

&#x1f504; MVC 交互流程 #mermaid-svg-5xGt0Ka13DviDk15 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-5xGt0Ka13DviDk15 .error-icon{fill:#552222;}#mermaid-svg-5xGt0Ka13DviDk15 .error-text{fill:#552222…...

【方案分享】蓝牙Beacon定位精度优化(包含KF、EKF与UKF卡尔曼滤波算法详解)

蓝牙Beacon定位精度优化&#xff1a;KF、EKF与UKF卡尔曼滤波算法详解 标签&#xff1a;蓝牙定位&#xff5c;Beacon&#xff5c;卡尔曼滤波&#xff5c;UKF&#xff5c;EKF&#xff5c;RSSI&#xff5c;室内定位&#xff5c;滤波算法&#xff5c;精度优化 相关分享&#xff1a;…...

新能源汽车热管理核心技术解析:冬季续航提升40%的行业方案

新能源汽车热管理核心技术解析&#xff1a;冬季续航提升40%的行业方案 摘要&#xff1a;突破续航焦虑的关键在热能循环&#xff01; &#x1f449; 本文耗时72小时梳理行业前沿方案&#xff0c;含特斯拉/比亚迪等8家车企热管理系统原理图 一、热管理为何成新能源车决胜关键&am…...

LeetCode 239. 滑动窗口最大值(单调队列)

题目传送门&#xff1a;239. 滑动窗口最大值 - 力扣&#xff08;LeetCode&#xff09; 题意就是求每个窗口内的最大值&#xff0c;返回一个最大值的数组&#xff0c;滑动窗口的最值问题。 做法&#xff1a;维护一个单调递减队列&#xff0c;队头为当前窗口的最大值。 设计的…...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1开通指南及使用心得

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;CSDN领军人物&#xff0c;全栈领域优质创作者✌&#xff0c;CSDN博客专家&#xff0c;阿里云社区专家博主&#xff0c;2023年CSDN全站排名top 28。 &#x1f3c6;数年电商行业从业经验&#xff0c;AWS/阿里云资深使用用…...

鸿蒙图片缓存(一)

移动端开发过程中图片缓存功能是必备&#xff0c;iOS和安卓都有相关工具库&#xff0c;鸿蒙系统组件本身也自带缓存功能&#xff0c;但是遇到复杂得逻辑功能还是需要封装图片缓存工具。 系统组件Image 1. Image的缓存策略 Image模块提供了三级Cache机制&#xff0c;解码后内…...

运行示例程序和一些基本操作

欢迎 ----> 示例 --> 选择sample CTRL B 编译代码 CTRL R 运行exe 项目 中 Shadow build 表示是否 编译生成文件和 源码是否放一块 勾上不在同一个地方 已有项目情况下怎么打开项目 方法一: 左键双击 xxx.pro 方法二: 文件菜单里面 选择打开项目...

学习数字孪生,为你的职业发展开辟新赛道

你有没有想过&#xff0c;未来十年哪些技能最吃香&#xff1f; AI、大数据、智能制造、元宇宙……这些词频繁出现在招聘市场和行业报告中。而在它们背后&#xff0c;隐藏着一个“看不见但无处不在”的关键技术——数字孪生&#xff08;Digital Twin&#xff09;。 它不仅在制造…...

WebRTC源码线程-1

1、概述 本篇主要是简单介绍WebRTC中的线程&#xff0c;WebRTC源码对线程做了很多的封装。 1.1 WebRTC中线程的种类 1.1.1 信令线程 用于与应用层的交互&#xff0c;比如创建offer&#xff0c;answer&#xff0c;candidate等绝大多数的操作 1.1.2 工作线程 负责内部的处理逻辑&…...

python学习打卡day47

DAY 47 注意力热图可视化 昨天代码中注意力热图的部分顺移至今天 知识点回顾&#xff1a; 热力图 作业&#xff1a;对比不同卷积层热图可视化的结果 # 可视化空间注意力热力图&#xff08;显示模型关注的图像区域&#xff09; def visualize_attention_map(model, test_loader,…...

MySQL中的内置函数

文章目录 一、日期函数1.1 获取当前的日期1.2 获取当前时间1.3 获取当前日期和时间1.4 提取时间日期1.5 添加日期1.6 减少日期1.7 两个日期的差值 二、字符串处理函数2.1 获取字符串的长度2.2 获取字符串的字节数2.3 字符串拼接2.4 转小写2.5 转大写2.6 子字符串第⼀次出现的索…...

Ansible自动化运维全解析:从设计哲学到实战演进

一、Ansible的设计哲学&#xff1a;简单即正义 在DevOps工具链中&#xff0c;Ansible以其"无代理架构&#xff08;Agentless&#xff09;"设计独树一帜。这个用Python编写的自动化引擎&#xff0c;通过SSH协议与目标主机通信&#xff0c;彻底摒弃了传统配置管理工具…...

YOLOv8n行人检测实战:从数据集准备到模型训练

YOLOv8n行人检测实战&#xff1a;从数据集准备到模型训练 一、为什么选择YOLOv8&#xff1f;二、环境准备2.1 环境配置解析 三、安装Ultralytics框架四、数据集准备与理解4.1 数据集下载4.2 数据集结构4.3 YOLO标签格式解析 五、数据集可视化&#xff1a;理解标注数据5.1 可视化…...

国标GB28181设备管理软件EasyGBS远程视频监控方案助力高效安全运营

一、方案背景​ 在商业快速扩张的背景下&#xff0c;连锁店门店数量激增&#xff0c;分布范围广。但传统人工巡检、电话汇报等管理方式效率低下&#xff0c;存在信息滞后、管理盲区&#xff0c;难以掌握店铺运营情况&#xff0c;影响企业效率与安全。网络远程视频监控系统可有…...

网络寻路--图论

所以我们固定题中M条边&#xff08;因为这M条一定联通&#xff09; P8605 [蓝桥杯 2013 国 AC] 网络寻路 - 洛谷 #include<bits/stdc.h> using namespace std; #define N 100011 typedef long long ll; typedef pair<int,int> pii; int n,m; int d[N],u[N],v[N]…...