时间复杂度和空间复杂度
前段时间在学数据结构的时候,就提到过关于时间复杂度和空间复杂度的,当时就想,还有什么是计算机算不出来的,就觉得这东东很水 ,事实上确实有那么一点点水,但是这几天在刷题的时候,发现有些题后面对程序的时间复杂度和空间复杂度还有要求,什么时间复杂度为O(N),空间复杂度为O(1)什么的,想想这块还是整理一下的好。,
首先,平常我们做一件事,我们总是在想有没有更好的方法,可不可以少做点什么而不影响结果的,其实还是懒 ,就像上课的时候,从宿舍楼到教室,总是在想有没有一条最近的路,可以省时间的(时间复杂度),下课的时候,又在想有没有哪一条路的人少一点,顺畅一些,可以更快的离开(空间复杂度)。。。。总的来说,还是离不开两个字–>效率。
时间复杂度
同样的,计算机也有效率这个概念,平时在运行代码的时候,计算机跑的飞快,很难判断两个算法的快慢,那么C语言里Sleep这个函数就可以判断下了,作用就是让程序在运行期间睡个觉,跑慢一点,这样的话,两个时间复杂度不同的代码,快慢就显示出来了,好坏立见;计算机跑的那么快,执行速度都是每毫秒多少数据的那种,那么宏观上用执行时间来判断算法的好坏有点小困难,这时候就从微观上入手,一个算法所花费的时间与其中语句的执行次数成正比的关系,以算法的执行次数来算时间,执行多少次,他的时间复杂度就是多少就显得很直观。
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;
}
printf("%d\n", count);
}
直观上看,算法的执行次数 F(N)=N^2+2*N+10 *但是真正表示算法的时间复杂度的时候,却没有见到过有这么具体的数字,都是O(N),O(N ^2),为什么会这样呢,同是O(N)的时间复杂度,他们的执行次数真的一样吗?先打个比方,假如你是一个亿万富翁,去买一瓶矿泉水~~(不考虑为什么买的是矿泉水 )~~ ,会不会给店家说找99块钱,应该不会吧,估摸着他等店家找钱的时间,已经可以赚回来好几倍的99了。。。。。同样的,时间复杂度的计算也是如此,N^2和2**N,看起来好像差别不大,但是当N非常大的时候,前者比后者就大很多了,时间复杂度表示的只是一个大概的次数,把执行次数写的具体一点的话,就显得有点不好了,这个时候往往选择以偏概全,选择最大的一个执行次数,即大O的渐进表示法。上面所示的时间复杂度即为O(N ^2)。
到了我们最熟悉的冒泡排序法了
void sort(int* a,int len)
{
int i=0;
int j=0;
for(i=1;i<len;i++)
{
int flag=1;
for(j=0;j<len-i;j++)
{
if(a[j]>a[j+1])
{
int t=a[j];
a[j]=a[j+1];
a[j+1]=t;
flag=0;
}
}
if(flag==1)
{
break;
}
}
}
通常来说,运气好一点的话,冒泡排序的时间复杂度就是O(N),运气不好就是O(N ^2)。。。。。表示一个算法的时间复杂度应该如何选择呢,肯定不能选最好的情况,这时候以偏概全就显得不是那么的好,只能用最坏的情况来代表算法真正的时间复杂度。
二分查找呢(两种二分查找的方式)
//[left,right]
int BinarySearch(int* a, int left, int right, int x)
{
while (left <= right)
{
int mid = (left + right) / 2;
if (a[mid] == x)
{
return mid;
}
else if (a[mid] > x)
{
right = mid + 1;
}
else
{
left = mid - 1;
}
}
return -1;
}
//[left,right)
int BinarySearch2(int* a, int left, int right, int x)
{
while (left < right)
{
int mid = (left + right) / 2;
if (a[mid] == x)
{
return mid;
}
else if (a[mid] > x)
{
right = mid;
}
else
{
left = mid+1;
}
}
return -1;
}
怎么说呢,运气最好的情况就是要找的数就在中间,一次到位,但是表示肯定不能这样来,因为不能保证没一次都中奖吧,二分查找的时间复杂的O(log2 N) log以2为底 N的对数次(表示数学都还给老师了一时间没看明白这什么意思 )
还有以前的数学题,斐波那契数的递归算法
int Fibonacci(int num)
{
if(num<3)
{
return 1;
}
return Fibonacci(num-1)+Fibonacci(num-2);
}

简单的一个斐波那契数,用递归写真的正确吗,递归的内部就是一个树状的结构,他的每一个结果都是一步一步单独计算出来的,每一个结点都是一个单独的计算,既耗费时间还耗费空间,而且当要计算的数字大于50的时候,程序就会跑的比较慢了,不能立刻出结果的那种。递归的时间复杂度是O(N ^2)
而递推就不一样了
#include<stdio.h>
int main()
{
int f1 = 1, f2 = 1, f3 ;
int i = 0;
int num;
scanf("%d",&num);
for (i = 3; i <= num; i++)
{
f3= f1 + f2 ;
f1 = f2;
f2 = f3;
}
printf("%d", f3);
return 0;
}
递推的时间复杂度便是O(N),计算大数据明显比递归快很多。
空间复杂度
空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度 。空间复杂度不是程序占用了多少bytes的空间,因为这个也没太大意义,所以空间复杂度算的是变量的个数。
一般来说,只要算法不涉及到动态分配空间,递归以及栈帧,空间复杂度都是O(1);
给定一个数组 nums 和一个值 val,你需要原地移除所有数值等于 val 的元素,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。元素的顺序可以改变。你不需要考虑数组中超出新长度后面的元素。
int removeElement(int* nums, int numsSize, int val){
int i=0;
int src=0;
for(i=0;i<numsSize;i++)
{
if(nums[i]!=val)
{
nums[src]=nums[i];
src++;
}
}
return src;
}
给定一个排序数组,你需要在原地删除重复出现的元素,使得每个元素最多出现两次,返回移除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
int removeDuplicates(int* nums, int numsSize){
int i=0;
int len=0;
int j=0;
if(numsSize==0)
{
return 0;
}
for(i=0;i<numsSize-1;i++)
{
if(nums[i]==nums[i+1]&&(len<2))
{
nums[j]=nums[i];
j++;
len++;
}
if(nums[i]!=nums[i+1]&&(len<2))
{
nums[j]=nums[i];
j++;
len=0;
}
else if(nums[i]!=nums[i+1]&&(len>=2))
{
len=0;
}
}
if(len<2)
{
nums[j]=nums[i];
j++;
}
return j;
}

1万+

被折叠的 条评论
为什么被折叠?



