重要的内容我都用彩笔标注了,易错点我也在文章中提醒了大家,大家放心食用。
目录
1、前言
算法在编写成.exe文件后,运行时需要耗费时间资源和空间资源。我们在衡量一个算法的好坏的时候,一般是从时间和空间两个角度去考察,即时间复杂度和空间复杂度。我们在做面试题时,很多题目会对这两个复杂度(尤其是时间复杂度)进行约束,因此我们要懂得如何去计算时间复杂度,才能控制和约束我们所写代码的时间和空间复杂度。
2、时间复杂度
2.1 时间复杂度的概念
算法的时间复杂度是一个函数(数学中的函数表达式),而算法中的基本操作的执行次数,则为算法的时间复杂度。
练习一下(づ ̄3 ̄)づ╭❤~
第一个双重循环的执行次数为N*N,第二个循环的执行次数是2*N,第三个while循环的执行次数10,Func1的时间复杂度精确为F(N)= N*N+2*N+10。
但是准确的时间复杂度函数式,不方便在算法之间比较,我们应该对时间复杂度进行大概估算,这样才方便比较。
2.2 大O的渐进表示法
大O符号:是用于描述函数渐进行为的数学符号。
推导大O阶方法:
- 用常数1取代运行时间中的所有加法常数
- 在修改后的运行次数函数中,只保留最高阶项
- 如果最高阶项存在且不是1,则去除与这个项目相乘的常数,得到的结果就是大O阶
使用大O的渐进表示法以后,Func1的时间复杂度为O()
常见的函数的时间复杂度为O(1)、O(N)、O()、O(logN)
注意:最后一个的写法,因为在文本中对数不好书写,我们常简写成logN,最好不要写成lgN,我们只需要在看到lgN能知道讲的是logN就行了。
开始练习用大O渐进法算时间复杂度:
练习一:
答案为:O(N);
练习二:
答案为:O(N+M)(如果N>>M,答案就写O(N),如果M>>N,答案就写O(M), M N,答案就写O(N)/ O(M));
练习三:
答案:O(1);
练习四:
答案:O(N);
补充一点:有些算法的时间复杂度存在最好、平均和最坏情况,在实际中一般情况关注的是算法的最坏运行情况。
像练习四中:最好的情况是一次就可以找到,时间复杂度就是O(1),最坏的情况需要N次才能找到,时间复杂度就是O(N),平均情况是需要N/2次找到,我们只关注最坏情况,时间复杂度为O(N)。
练习五:
答案:O()(提示:考虑最坏的情况,F(N)= N-1+N-2+N-3+......+1=N*(N-1)/2 )
练习六:
答案:O(logN);
练习七:
答案:O(N)
练习八:
答案:O(2^N);
2^0+2^1+......+2^(N-2)+2^(N-1)=2^(N-1)-1 ,用大O的渐进表示法:O(2^N)
这里我说一嘴,这个代码并没有什么意义,因为执行效率太低了,O(2^N)执行次数非常大(N趋于无穷,2^N>N^2>N>logN),很容易造成stack overflow(栈溢出),我们一般不会这样写Fibonacci,下面是优化版本。
long long Fibonacci(size_t N)
{
int f1 = 1, f2 = 1,f3;
for (int i = 3; i <= N; i++)
{
f3 = f1 + f2;
f1 = f2;
f2 = f3;
}
if (N < 3)
return 1;
else
return f3;
}
这个的时间复杂度为O(N),大大提高了执行效率。
3、空间复杂度
空间复杂度也是一个数学表达式,是对一个算法在运行过程中临时占用存储空间大小的量度。空间复杂度并不是计算程序占用了多少bytes的空间,而是计算额外开辟变量的个数,与时间复杂度相同,都采用大O渐进表示法。
注意:计算空间复杂度,一定要记住一句话“空间可以叠加,时间不可以叠加”。
先上几个题练一练:
练习一:
注意:空间复杂度计算的是主动创建的变量个数,被动接受的变量不需要计算
练习二:
答案:O(N);
练习三:
答案:O(N);
练习四:
答案:O(N);(时间是不可以叠加,空间是可以叠加的)
练习五:
答案:O(N)
这里我又要插一嘴(千万别想错辽),f1额外占用N+2个空间,main函数也是额外占用N+2个空间,因为第一次调用f1和第二次调用f1占用的空间都是一样的,空间可以叠加。
4、复杂度的oj练习题:
3.1 消失的数字
OJ链接:面试题 17.04. 消失的数字 - 力扣(LeetCode)
答案:
采用位运算,在循环时如果该数没有缺失,则a会与其进行两次异或运算,这两次则会抵消,但如果缺失了该数,最后的a就是所缺失的数(如果不懂,可以在评论区讨论)
3.2 轮转数组
方法一(最基础的):
//方法一:
void rotate(int* nums, int numsSize, int k)
{
for (int i = 1; i <= k % numsSize; i++)
{
int tmp = nums[numsSize - 1];
int n = numsSize;
while (n-1)
{
nums[n - 1] = nums[n - 2];
n--;
}
nums[0] = tmp;
}
}
这个能达到左旋的效果,但是不符合时间限制的
方法二(yyds):
void reverse(int* a,int left,int right)
{
int tmp=0;
while (left <= right)
{
tmp = a[left];
a[left] = a[right];
a[right] = tmp;
left++;
right--;
}
}
void rotate(int* nums, int numsSize, int k)
{
k = k % numsSize; //这个可别忘了
reverse(nums, numsSize - k, numsSize - 1);
reverse(nums, 0, numsSize - k - 1);
reverse(nums, 0, numsSize - 1);
}
数据结构很难,但我们一定要坚持住呀!
文章到此结束,喜欢我的文章的朋友点个赞再走吧!(更新不易,Thanks♪(・ω・)ノ)