什么是算法的复杂度
1.算法在编写成可执行程序后,运行时需要耗费时间资源和空间(内存)资源。因此衡量一个算法的好坏,一般是从时间和空间两个维度来衡量的,即时间复杂度和空间复杂度。
2.时间复杂度主要衡量一个算法的运行快慢,而空间复杂度主要衡量一个算法运行所需要的额外空间在计算机发展的早期,计算机的存储容量很小。所以对空间复杂度很是在乎。但是经过计算机行业的迅速发展,计算机的存储容量已经达到了很高的程度(摩尔定律,每18个月半导体晶体管就会翻一倍)。所以我们如今已经不需要再特别关注一个算法的空间复杂度。
时间复杂度
时间复杂度的定义:在计算机科学中,算法的时间复杂度是一个数学函数表达式,它定量描述了该算法的运行时间。个算法执行所耗费的时间,从理论上说,是不能算出来的,只有你把你的程序放在机器上跑起来,才能知道。但是我们需要每个算法都上机测试吗? 是可以都上机测试,但是这很麻烦,所以才有了时间复杂度这个分析方式。一个算法所花费的时间与其中语句的执行次数成正比例,算法中的基本操作的执行次数,为算法的时间复杂度。
即: 找到某条基本语句与问题规模N之间的数学表达式,就是算出了该算法的时间复杂度。
空间复杂度
空间复杂度也是一个数学函数表达式,是对一个算法在运行过程中临时占用存储空间大小的量度。
空间复杂度不是程序占用了多少bytes的空间,因为这个也没太大意义,所以空间复杂度算的是变量的个数。
空间复杂度计算规则基本跟实践复杂度类似,也使用大O渐进表示法。
注意:函数运行时所需要的栈空间(存储参数、局部变量、一些寄存器信息等)在编译期间已经确定好了,因此空间复杂度主要通过函数在运行时候显式申请的额外空间来确定。
大O渐进表示法
大O符号 (Big o notation): 是用于描述函数渐进行为的数学符号推导大O阶方法:
1、用常数1取代运行时间中的所有加法常数
2、在修改后的运行次数函数中,只保留最高阶项。
3、如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶。
for(int i=0;i<n;i++)
{
for(int j=0;j<n;j++)
{
printf("%s",hello world!);
}
}
for(int i=0;i<2*n;i++)
{
printf("%s",hello world);
}
for(int i=o;i<10;i++)
{
printf("%s",hello world);
}
此函数的时间复杂度函数为nn+2n+10,
n越大后两项对结果的影响越小,而我们其实想要的并不是精确的执行次数。
故这里可以用大O的渐进表示法(估算),这里函数的时间复杂度就是O(n*n)
leetcode 268.丢失的数字
条件将时间复杂度控制在O(n)内
int missingNumber(int* nums, int numsSize)
{
int x=0;
for(int i=0;i<=numsSize;i++)
{
x^=i;
}
for(int i=0;i<numsSize;i++)
{
x^=nums[i];
}
return x;
}
可以有很多种方案,这里只展示异或的方法
旋转数组
给定一个数组,将数组中的元素向右移动 k个位置,其中 是非负数
输入: nums = [1,2,3,4,5,6,7],k = 3
输出: [5,6,7,1,2,3,4]
解释:
向右旋转 1 步: [7,1,2,3,4,5,6]
向右旋转 2 步:[6,7,1,2,3,4,51
向右旋转 3 步: [5,6,7,1,2,3,4]
进阶:
尽可能想出更多的解决方案,至少有三种不同的方法可以解决这个0
问题。你可以使用空间复杂度为 O(1)的 原地 算法解决这个问题吗?
思路1:暴力求解,旋转k次
空间复杂度O(1),时间复杂度O(n*k)
思路2:开辟额外空间(用空间换时间)
空间复杂度O(n),时间复杂度O(n)
思路3:大佬思路
输入: nums = [1,2,3,4,5,6,7],k = 3
输出: [5,6,7,1,2,3,4]
4 3 2 1 5 6 7 前n-k个逆置
4 3 2 1 7 6 5 后k个逆置76
5 6 7 1 2 3 4 整体逆置
这里只展现大佬思路
void Reverse(int*nums,int left,int right)
{
while(left<right)
{
int tmp=nums[left];
nums[left]=nums[right];
nums[right] = tmp;
++left;
--right;
}
}
void rotate(int* nums, int numsSize, int k)
{
if(k >= numsSize)
k %= numsSize;
// 前n-k个数逆置
Reverse(nums,,numsSize-k-1);
// 后k个逆置
Reverse(nums, numsSize-k, numsSize-1);
// 整体逆置
Reverse(nums,,numsSize-1);
}