数据结构 时间与空间复杂度就看这篇了【生活经历 + 实例讲解】_生活复杂度(1)

img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化资料的朋友,可以戳这里获取

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

}


* 首先来看外层循环,执行了N次,对于每一次的外层执行,都对应着N次的内层循环执行,因此其复杂度就是O(N2),下面的我们实例一算过了,为2N + 10,因此总体的时间复杂度为【N2 + 2N + 10】
* 我们继续来通过法则简化一下,首先根据`法则一`,将10改为1,然后根据`法则四`,将1去除,接着根据`法则二`,只保留高阶项,对于谁高谁低,我在前面已然说过,若是遗忘,往上翻阅即可,最后可以看到,只剩下N2,前面并没有任何系数,所以不需要化简,最后可以得出这个程序的总体时间复杂度为**O(N2)**


**实例三**


* 然后来看第三个



// 计算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);
}


* 很明显,两个循环,第一个循环的执行次数是M次,第二个循环的执行次数是N次,所以将它们加起来即可,就是O(M + N),这里若是M == N,则可表示为O(2M)然后转化为O(M),但是这里注明了两个变量的大小是不同的,所以整体时间复杂度为**O(M + N)**,无需化简


**实例四**


* 这题很简单,送分,循环执行了100次,因此时间复杂度为O(100),根据`法则一`,可将这个100化为1,因此最终的时间复杂度是一个常数阶**O(1)**



// 计算Func4的时间复杂度?
void Func4(int N)
{
int count = 0;
for (int k = 0; k < 100; ++ k)
{
++count;
}
printf(“%d\n”, count);
}


**实例五**



// 计算strchr的时间复杂度?
const char * strchr ( const char * str, int character );


* 这个函数名是C语言里面的一个字符串操作函数,带你们看看
* 根据描述可以知道,是查找在字符串中出现的第一个字符



> 
> ![在这里插入图片描述](https://img-blog.csdnimg.cn/cc47d2f9a1ab41d7a0626ca4d374c2af.jpeg#pic_center)
> 
> 
> 


* 可以看到,这个算法的时间复杂度其实和在一个数组中查找一个数是一样的,但是我们还是要去取最坏的情况,也就是O(N),设想你取的是平均的时间复杂度O(N/2),但是需要查找的这个数再后面的一部分的时候,会超过O(N/2)了,此时就显得不精准  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/d8b49c4818504b4ca3c5e91a2387f782.jpeg#pic_center)


#### 📰生活哲理闲谈




---


例子讲了一大半了,不知道你对时间复杂度的计算有没有一个基本的掌握了,可能有些同学对这个法则还不太熟悉,那你要现将这个法则仔细的研读一遍,思考一下再去做题🤔


* 其实对于这个时间复杂度呢,它本质上算的并不是时间,而是一个**执行的结果次数**,法则中我们有提到只保留这个高阶项,**但是为什么只保留高阶项呢**,这个其实你还是要去联想我们生活中的例子


1. 假设你是一个富豪,已经坐拥百万的家产,每天只需要轻松玩乐即可,那这个时候有三笔生意同时与你又进行了签订,一个是10亿,一个是1千万,另一个是10万,那你会看重哪个呢?不用说,一定是那个10亿,因为你已经足够有钱,不会去看重那些小财富,**而是只会盯紧金额最大的那一部分**
2. 近些年科学家发现了在银河系外有着一些奇特的星系,有些离我们10光年,有些呢则是离我们几百光年,但是这对你来说区别又在哪儿呢,无论是10光年还是几百光年之外的星系,距离你都是非常遥远的距离




---


#### 🗡实例6~10讲解



> 
> 好,经过一些闲谈后我们继续来看实例,相信你对时间复杂度的求解有了一个层面的理解
> 
> 
> 


**实例六**


* 第六个,就是大家最熟悉也是最用的最多的一种排序算法——冒泡排序,如果想看其他排序的可以看看我的这篇文章 [链接](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)
* 大家可能完全性地认为其时间复杂度为**O(N2)**,那你觉得这是它的最好、最坏还是平均呢?我们一起来分析一下



// 计算BubbleSort的时间复杂度?
void BubbleSort(int* a, int n)
{
//[0,n - 1)
for (int i = 0; i < n - 1; ++i)
{
int changed = 0;
for (int j = 0; j < n - 1 - i; ++j)
{
if (a[j] > a[j + 1])
{
swap(&a[j], &a[j + 1]);
changed = 1;
}
}
if (changed == 0)
break;
//PrintArray(a, n);
}
}



> 
> ![在这里插入图片描述](https://img-blog.csdnimg.cn/ac93e5a3826e42a1bc71ee9d99795b2f.jpeg#pic_center)
> 
> 
> 


* 好,我们抛开上面的代码来分析一下,因为大家可能看到两层for循环就本能地认为这是一个O(N2)的时间复杂度
* 通过上面的图我们可以看出,有N个数字,那这个外层就会执行N - 1次,对于每一次的循环,会进行一个内部的比较,通过两两之间的不断比较,将这个最大或者最小的数字冒到最后面,这其实就是冒泡排序的本质所在,然后对于第一次的比较会比较`N - 1`次,第二次呢会比较`N - 2`次,第三次会比较`N - 3`次。。。依次类推,到了最后只会比较1次
* 我们看时间复杂度看到不是有几层循环,而是看这个程序它运行了多少次,上面我们分析到,这个每次的比较次数是【N - 1】到【N - 2】。。。最后到【1】,因此可以看出这是呈现一个**等差数列的递减形式**可以使用等差数列的求和公式去计算总共比较了多少次,(N - 1)\*N/2,最后根据我们的法则二、法则四,便可以得出最终的时间复杂度为O(N2)
* 冒泡排序的时间复杂度是这么算出来的,你明白了吗❓


**实例七**


* 实例七是关于二分查找的例子,有关二分查找相信大家都是蛮熟悉,是一种比较高效的查找算法,但是你知道它的时间复杂度是多少吗?我们一起来分析一下



// 计算BinarySearch的时间复杂度?
int BinarySearch(int* a, int n, int x)
{
assert(a);
int begin = 0;
int end = n-1;
// [begin, end]:begin和end是左闭右闭区间,因此有=号
while (begin <= end)
{
int mid = begin + ((end-begin)>>1);
if (a[mid] < x)
begin = mid+1;
else if (a[mid] > x)
end = mid-1;
else
return mid;
}
return -1;
}


* 首先我们再来简述一下它的原理,通过两端的边界,每次取寻找这个中间值,然后去比较待查找数字和中间值的关系,以此来确定待查数字在给定数组的待查数据范围,所以二分法每次范围都是以**一半一半缩小的**,大概是下面这样👇


![在这里插入图片描述](https://img-blog.csdnimg.cn/6b4f391b118a48b1bf92ed4253bbcad2.jpeg#pic_center)


* 我们需要求的是使用这个二分法去查找这个数时所需要的次数👈,这点首先你要明确
* 然后根据二分法的特性,每次都是以一半一半呈现一个折半的查找,然后直到总的数字个数被除到1为止,从我上面的式子可以看到,N是题目给出的数字长度个数,最后的结果我们知道会走到1,那唯一不知道的就是到底除了几个2,也就是**这个2上的指数是多少**
* 我们将其设置为x,然后你自己在草稿纸上列一个式子,就可以求出这个X的表达式,是`log以2位底N的对数`,然后你去联想上面我所给出的O(log2N)的时间复杂度的图,就可以很明确的知道二分法的时间复杂度是多少了
* 对于这个log以2为底的这个2,其实在很多资料书籍上都会将其省去,因为这个对总体不存在影响,所以会简写成logN,但是最好不要再简化写成lgN,不太建议写成这样,容易产生误会



> 
> 对于这个二分法,其实它是蛮高的,对比一般的暴力查找,可谓是**天壤之别**,我们通过一些数据来对比一下
> 
> 
> 


![在这里插入图片描述](https://img-blog.csdnimg.cn/df44e64c0b2c4ab6b0fe0d72b4962b18.jpeg#pic_center)


* 可以看到,随着数据量的增加,暴力查找需要的100W次、10亿次,但是对二分查找来说只需要10次20次就够了,因为从logN这个图像其实可以看出,越到后面它的坡度越加平缓,数据的爬升量也是慢慢地增加而已
* 所以二分法其实还是挺牛逼的。但是为什么有些地方在查找数据的时候不使用二分查找,而是使用效率更高的**哈希表或是红黑树**呢,因为二分查找它是有一个条件限制的,需要在待查找元素已经有序的情况下才可以进行一个折半查找,所以就一定要对无序的数组进行一个排序
* 但是我们知道,对于排序的话,也是需要时间复杂度的,例如说直接使用个快排也是要O(NlogN)的时间复杂度,所以对于二分法有着一定的容错率,因此在某些场合下就会使用其他的搜索算法


![在这里插入图片描述](https://img-blog.csdnimg.cn/1c019cb8851f413f93f169ba06df3c8a.png#pic_center)  
 **实例八**


* 接着我们来讲的是一个阶乘递归的时间复杂度计算。对于递归,其实我们不仅仅要考虑的是本次的执行次数,而且还要考虑递归的次数,所以这里先给出求解递归算法时间复杂度的定义


递归时间复杂度计算方法和技巧 —— 每次递归调用的执行次数累加



// 计算阶乘递归Fac的时间复杂度?
long long Fac(size_t N)
{
if(1 == N)
return 1;
return Fac(N-1)*N;
//算的是每次里面的执行可以是常数次
//每次是常数次,调用了N次,N个1相加
}


* 可以看到,对于时间复杂度,计算的还是执行的次数,但是对于递归呢,是一个**累加**的效果,因为既然是递归了,次数也是需要计算进去
* 就像上面这题对于一次递归,内部只有一个if条件分支判断,因此单次的时间复杂度为O(1),是一个常量级别的。然后递归调用了N次,所以我们进行一个常量级别的求和接口也就是N个1相加,那也就是N
* **因此这个算法的时间复杂度为O(N)**


可能还有同学没看懂,我通过一张图给你看看。可以看到,每一次递归的调用,执行次数都只有一次,那随着N次递归,就有N - 1次调用,我们去计算总的执行次数就行


![在这里插入图片描述](https://img-blog.csdnimg.cn/79002f765c9a4f3e83658e6d14dcd36f.jpeg#pic_center)


**实例九**


* 然后我们再来看一个,是上面一个案例的改进版



long long Fac(size_t N)
{
if (1 == N)
return 1;
for (size_t i = 0; i < N; ++i)
{
//…
}
return Fac(N - 1) * N;
//递归了N次,每次里面循环走了N次 --》等差数列
// N + (N - 1) + (N - 2)…+ 1 = N^2
//函数调用不算次数,算的是里面的东西
}


* 可以看到,在递归的内部我增加了一个循环,那我想你已经可以抢答了:递归调用了N次,每次的N变化到N-1,再变化到N-2,也就是每一个单层递归的执行次数在不断发生变化,最后递归到1为止
* 然后我们还是一样,去累计这个每次递归调用的执行次数`N + (N - 1) + (N -2)... + 1 = (1 + N) * N/2`,最后根据法则就可以将其化简为O(N2),相信你此时已经是非常熟练了


一样是通过图解来分析一下,很直观可以看出,每次的执行次数呈现一个递减的趋势,直到递归到1为止,将递归次数累加即可  
 ![在这里插入图片描述](https://img-blog.csdnimg.cn/18de78015b05494495319c382fe024ca.jpeg#pic_center)  
 **实例十**


* 压轴的例题,自然是留给我们开头就给出的那个**裴波那契数列的递归求解**



// 计算斐波那契递归Fib的时间复杂度?
long long Fib(size_t N)
{
if(N < 3)
return 1;
return Fib(N-1) + Fib(N-2);
}


* 在开头我们就讲了,它的时间复杂度为O(2N),但是要怎么去计算呢?我们可以先通过画图来分析一下



> 
> ![在这里插入图片描述](https://img-blog.csdnimg.cn/655c4c5ac7fb4d6b93601d421476dc8f.jpeg#pic_center)
> 
> 
> 


* 从上图可以看到,这个斐波那契函数的递归模型很像是一棵[二叉树](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)。当一次Fib()函数执行的之后,直到递归到最后一层,也就是我们常说的**叶子结点**时,便递归结束,实现一个回调。可以看到,每次的递归调用都会分出来两个新的数,和细胞分裂也很类似,第一层是1个,第二层是2个,第三层是4,第四层是8个。。。用2的指数次表示就可以写成20、21、22。。。
* 因为这个递归次数会调用N - 1次,所以最后一层就是2N-1个数字。从中我们可以得出一个表达式为【20+21+22+23+…2(N-2) = 2(N-1) - 1】
* 然后根据我们的这个法则,就可以得出它的时间复杂度为**O(2N)**


**但是它的空间复杂度是多少你知道吗?继续看下去你就知道了**




---


## ⌚空间复杂度



> 
> 说完了时间复杂度,接下去我们来说说空间复杂度
> 
> 
> 


### 🌳概念


* 首先一样,来讲讲空间复杂度的概念



> 
> 空间复杂度也是一个数学表达式,是对一个算法在运行过程中`临时占用`存储空间大小的量度
> 
> 
> 


* 空间复杂度不是程序占用了多少bytes的空间,因为这个也没太大意义,所以空间复杂度算的是变量的个数。空间复杂度计算规则基本跟时间复杂度类似,也使用**大O渐进表示法**


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


### 🌳例题分析



> 
> 一些概念性的东西我们在时间复杂度已经详细介绍过了,这里就不再重复,直接通过例题来了解空间复杂度如何计算,这里的很多例题也就是上面的哪一些
> 
> 
> 


**上面也有说到过,对于空间复杂度,一般就是O(1)或O(N)**


#### 🔢普通函数


**实例一**


* 好,首先我们来看第一个简单一些的实例,这个实例我们前面有讲到过,它的时间复杂度为O(1),是常数阶的



// 计算Func1的空间复杂度?
void Func1(int N)
{
int count = 0;
for (int k = 0; k < 100; ++ k)
{
++count;
}
printf(“%d\n”, count);
}


* 然后我们来分析空间复杂度,概念中有说到了,对于空间复杂度呢,其实算的就是**变量的个数**,还有就是你除了函数给出的参数以外**额外去开辟的空间**
* 那对于上面这个题,开辟了哪些变量呢,就是count和循环变量k,那也就是O(2),然后根据我们的`法则一`,可以化为O(1),因此可以看出这段程序的时间与空间复杂度均为**O(1)**


**实例二**


* 实例二也是我们熟悉的冒泡排序,它的时间复杂度为O(N2),是平方阶。那空间复杂度呢?也是O(N2)吗?



// 计算BubbleSort的时间复杂度?
void BubbleSort(int* a, int n)
{
//[0,n - 1)
for (int i = 0; i < n - 1; ++i)
{
int changed = 0;
for (int j = 0; j < n - 1 - i; ++j)
{
if (a[j] > a[j + 1])
{
swap(&a[j], &a[j + 1]);
changed = 1;
}
}
if (changed == 0)
break;
//PrintArray(a, n);
}
}


* 肯定有同学认为函数形参中的这个数组a是需要占用空间的,然后数组中有n个元素,因此时间复杂度就为O(N)
* **这么去看其实是不对的**,我们上面说到了,对于空间复杂度的计算,应该是额外空间的计算,但是这个数组a和它的大小呢,在这个函数内部其实你可以看做是编译器已经给到你的,已经是存在了,不算做是额外的空间。在这里新增加的变量其实就是循环变量中的end和i,以及一个标志变量exchange,那和实例一等同,**它的空间复杂度其实也是O(1)**


**实例三**


* 下面一个案例也是有关斐波那契数列的求解,不过使用的是循环迭代的方法,那它的空间复杂度是多少呢?



// 计算Fibonacci的空间复杂度?
// 返回斐波那契数列的前n项
long long* Fibonacci(size_t n)
{
if(n==0)
return NULL;

 long long \* fibArray = (long long \*)malloc((n+1) \* sizeof(long long));
 fibArray[0] = 0;
 fibArray[1] = 1;
 for (int i = 2; i <= n ; ++i)
 {
 	fibArray[i] = fibArray[i - 1] + fibArray [i - 2];
 }
 return fibArray;

}


* 细心的小伙伴其实可以看出,为了实现存放斐波那契数列,去实现一个循环迭代,在开头使用malloc动态开辟了一个数组,因为对于`malloc`来说,是在堆内存中申请一块空间来使用,所以这个数组就是属于额外开辟的空间。
* 开的数组大小是有n + 1个,那其实根据我们的法则其实就可以看出了,**这个算法的空间复杂度为O(N)**


#### 🔢递归函数【递归在栈中的原理】



> 
> 上面三个实例呢,可以看出,比较简单一些,但是对于递归函数的空间复杂度,就没有那么容易分析,这个需要你去思考🤔
> 
> 
> 


**实例四**


* 首先我们先看到的也是上面说到过的阶乘递归
* 这段阶乘递归我们上面有讲到,使用递归次数累加可以计算得到时间复杂度为O(N),那这个空间复杂度呢?也是O(N)吗❓



// 计算阶乘递归Fac的空间复杂度?
long long Fac(size_t N) //此时需要考虑到这个递归函数的参数
{
if(1 == N)
return 1;
return Fac(N-1)*N;
}


* 从程序中可以看到并没有任何的变量被声明出来,那有同学问了,难道这个阶乘递归的空间复杂度为O(0)吗,其实并不是的,**对于递归函数,和普通函数计算空间复杂度其实是不同的**,我先给出定义,让你明白如何去计算递归函数的空间复杂度👇


递归调用空间复杂度计算方法和技巧 —— 每次递归调用的变量个数累加


* 对于递归的空间复杂度还要追溯到它在计算机里内存中的使用,在计算机内部呢,有一块区域叫做`栈内存`,对于栈的空间呢是很小的,编译器默认的栈空间大概只有1M左右,所以我们经常在写代码的时候会出现栈溢出【`Stack Over Flow`】这种错误,这个错误导致的原因其实就是你在栈中存放的东西太多了,就导致溢出了
* 栈这个东西呢,也是一个数据结构,我在其他文章中也有讲到 [链接](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb),不过数据结构这个栈和内存中的栈又是两个不同的东西,栈通俗一点来讲呢其实就像是一个水杯,你去接水,最多也就只能接到杯口,若是再接,那水就会漫出来,这其实就是**栈溢出**
* 那这个递归和栈又有什么区别呢?对于递归这种算法,它是需要在栈中开辟栈帧的,整体你看上去它很精简,但其实它内部的实现逻辑是蛮复杂的,上面我们画了一张二叉树的图形,递归其实也就是一层一层地向下调用,这个时候实现的其实就是一个嵌套的逻辑,那我们都知道**循环嵌套**,这个很明确,你套了几层那这个时间复杂度就是O(N)的几次方,随着嵌套的次数越来越多,这个逻辑也会越来越复杂,最后就会`导致时间复杂度过高然后超时`。那对于递归的调用其实也是一样的,只要你没有碰到递归出口,这个递归就会一层一层套下去,当你的数据量变得很大之后,栈帧开辟过多,也就导致了栈的溢出



> 
> 我这么解释不知道你懂了没有❓根据上面这段代码,我们再通过算法图示来看看
> 
> 
> 


![在这里插入图片描述](https://img-blog.csdnimg.cn/f96c1203b72f4260af5df67756d8915e.jpeg#pic_center)


* 那从图中其实就可以看出了这个原理,`随着这个递归函数每递归一次呢,就会在栈中开辟一块大小为N的空间`,然后直到递归结束为止,递归接口再根据一块块连续的存储空间再实行回调,其实它们最终使用的就只是**同一块存储空间**罢了,释放了在申请,释放了再申请。因为栈在内存中是**向下生长的**,`上面是低地址,下面是高地址`。你申请空间又释放,**每次拿的其实还是栈顶的那块空间**
* 我写一段程序给个运行结果你就知道为什么了(・∀・(・∀・(・∀・\*)



> 
> ![在这里插入图片描述](https://img-blog.csdnimg.cn/98b2540b4752423da9d6d281b9f936c1.jpeg#pic_center)
> 
> 
> 


* 可以看到,两个变量打印出来的地址是一样的,也就意味的这两个变量使用的内存地址是同一块,因为在Fun1()函数调用结束之后,它的函数栈帧就销毁了,使用过的空间销毁之后就将其还给操作系统了。此时又创建了一个Fun2()函数,此时操作系统就又会为其分配空间,为这个函数建立和初始化栈帧空间,此时它使用的便是上一次Fun1()释放掉的空间。从中也可以看出栈【先进后出】的原理,如果听起来有点抽闲的话,就看看[函数栈帧的创建和销毁吧!](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)
* 那一块块空间的累加,最后在根据`法则三`,就可以得到**空间复杂度为O(N)**


**实例五**


* 最后一个呢,就是我在上面所说到的有关【斐波那契数列】空间复杂度的求解,在这里给出一个答复



// 计算斐波那契递归Fib的空间复杂度?
long long Fib(size_t N)
{
if(N < 3)
return 1;
return Fib(N-1) + Fib(N-2);
}


* 对于斐波那契数列呢,其实和阶乘递归差不多,它要去实现的是一个空间的重复利用,我们还是通过递归算法图来看看
* 粗略地画了一下,它的递归原理大概就是这样,在逐层向下递归的时候,就会为每一个数开辟出来一块空间,然后在本次递归结束,调用下次,这个时候此次递归在栈中开辟的空间就会被销毁,然后下一层的递归调用又会来使用这块空间,那其实这块空间就是被每一次的递归调用反复使用的
* 举一个很形象的例子,可能有点味道,就是你去蹲坑🚽,你试用完了冲掉之后那下一个人人是不是还可以继续使用呢,那你们使用的就是同一个空间中的东西【doge】
* 所以我在图中指出的Fib(N-1)和Fib(N-2)其实使用的是同一块空间,只是上一次递归调用完成销毁之后,再进行一个反复利用罢了,因此只是开辟了一块空间。所以我们就可以得出了,这个空间复杂度为**O(N)**


### 在这里插入图片描述


好,到这里呢,我们对于时间复杂度和空间复杂度的基础学习就算是全部完结了,看到这里你要做到的就是拿到一道题,可以学会去分析这代码的执行逻辑和所消耗的空间大小,接下去我们通过两道LeetCode的题目来检验一下自己是否可以学以致用✏


## ⌚OJ题目实训



> 
> 以下两题请通过我另一个专栏【LeetCode算法题解】观看📺
> 
> 
> 


### ⌨【LeetCode】面试题17.04 - 消失的数字



![img](https://img-blog.csdnimg.cn/img_convert/c6f0a845e1f020ec53880654cb3ad2ec.png)
![img](https://img-blog.csdnimg.cn/img_convert/ca4d5af79c0f93a5c358da077a6f91d0.png)
![img](https://img-blog.csdnimg.cn/img_convert/0e7e7bcc71b4da2316fd1a9dad29b5f4.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**


### 在这里插入图片描述


好,到这里呢,我们对于时间复杂度和空间复杂度的基础学习就算是全部完结了,看到这里你要做到的就是拿到一道题,可以学会去分析这代码的执行逻辑和所消耗的空间大小,接下去我们通过两道LeetCode的题目来检验一下自己是否可以学以致用✏


## ⌚OJ题目实训



> 
> 以下两题请通过我另一个专栏【LeetCode算法题解】观看📺
> 
> 
> 


### ⌨【LeetCode】面试题17.04 - 消失的数字



[外链图片转存中...(img-aRuZAUwC-1715464483564)]
[外链图片转存中...(img-wfemQxyI-1715464483564)]
[外链图片转存中...(img-YMR4xD15-1715464483564)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新**

**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/forums/4f45ff00ff254613a03fab5e56a57acb)**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值