一、 如何评价一个算法的好坏 ,可以从时间与空间两方面来评判,解决相同的一个问题,不同的算法所用的时间和空间会有所差异,即:用时越短,占用空间越少的算法,则被称为好的算法。
二、复杂度分为:时间复杂度和空间复杂度
时间复杂度:算法所需要的时间越少,他的时间复杂度越低,但是由于现阶段硬件的种类多且不同,我们并不能精确且公平的测量算法程序运行的时间。因此,时间复杂度可以根据算法的基本操作的执行次数来判断。
空间复杂度:就是程序运行时所占用的空间大小(包括临时空间)这里我们暂且不做过多的讨论。
三、举例理解时间复杂度:
eg1 : 数组nums包含从0到n的所有整数,但其中缺了一个。
请编写代码找出那个缺失的整数。你有办法在O(n)时间内完成吗?
https://leetcode-cn.com/problems/missing-number-lcci/
方法一:公式计算
先将不缺失的0 ~ N 共N个数求和 为大和,再将缺失的0 ~ N 共N-1个数求和 为小和,
最后大和减去小和,就得到了缺失的数字。
(方法一:其基本操作共执行了 2n 次 , 其时间复杂度为 2n )
方法一:公式计算
int miss4(int* arr, int shu)
{
int sum = 0;
for (int i = 0; i <= shu; i++) //这个循环执行了 n 次(假定数列中共n个数)
{
sum += i;
}
for (int i = 0; i < shu; i++) //这个循环执行了 n 次
{
sum -= arr[i];
}
return sum;
}
方法二:排序+比较查找
先将缺失数列 进行从小到大排序,与第一种方法类似,再将0~N的每一位与排好序的缺失数列的每一位进行对比如果不相同则说明,这一位的数字缺失。(因为所对比的是0~N的定序数列,所以需要将原数列排序,达到一 一对应的效果)
此法的时间复杂度为:(n+(n-1)+(n-2)+(n-3)+…+1)+ n (排序的执行次数 + 遍历查找次数)
即为: (n^2+n) / 2 + n;
int miss(int* arr, int shu)
{
for (int i = 0; i < shu - 1; i++) //冒泡排序
{
for (int j = 0; j < shu - 1 - i; j++)
{
if (arr[j] > arr[j + 1])
{
int tem = 0;
tem = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = tem;
}
}
}
int k = -1;
for (int i = 1; i <= shu; i++) //遍历查找
{
if (arr[i - 1] != (i - 1))
{
k = i - 1;
break;
}
}
if (k == -1 && arr[0] > 1)
{
return arr[0] - 1;
}
else if (k == -1)
return arr[shu - 1] + 1;
return k;
}
eg2: 二分查找
二分查找的时间复杂度是: logN (以2为底的log N)
计算方式如下:二分查找是每次查找筛选掉一半的数据,所以设数据总数为:N
精确时间复杂度: 1 = N / 2 / 2 / 2 / 2 / 2 / ……; ==》 2^x = N (2的 x 次幂等于N)
这里的 x 相当于查找的次数:因为,每查找一次,下一次查找的数据数量就除以二
所以上式中 :x = logN (这种写法便于书写与理解) 即, 时间复杂度是:logN
char arr[] = { 1,2,3,4,5,6,7,9,10};
int left = 0; //定义首元素下标
int right = sizeof(arr) - 1; //定义尾元素下标
int mid = 0; //定义中间元素下标
int a = 0;
scanf("%d", &a); //输入要查找的值
for (left = 0; left <= right;) //开始查找
{
mid = left+(right-left)/2; //求出中间元素下标
if (a < arr[mid]) //将输入值与中间元素对比
{
right = mid - 1; //若小于中间元素,则向左缩小范围,即向左改变右下标
}
else if(a>arr[mid])
{
left = mid + 1; //同理,向右改变左下标
}
else
{
printf("找到了");
break; //若找到,则跳出循环
}
}
if (left > right) //判断是否完全查找完毕
————————————————
版权声明:本文为CSDN博主「MT_125」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/MT_125/article/details/122525067
四、复杂度表示
大O的渐进表示法(其主要思想是:用极限的思维,来去掉精确时间复杂度式子中对结果影响不大的项,进而简明的估算时间复杂度,使其易于观察和比较)
1.用常数 1 来表示运行时的所有加法常数
即如果一个算法的精确时间复杂度是能用 一个常数表示出来,那么,用大O的渐进表示法就表示为: O(1)
2.再修改后的运行次数函数中,只保留最高阶项
如;上题中方法二的算法:精确的时间复杂度为 (n^2+n) / 2 + n ;
项式中的 +n ,它对算法的影响随着计算次数的增加,越来越小,所以可以忽略不计。
所以用大O表示法表示为: O ( n^2 ) ;
3.如果最高项存在且不是 1 ,则除去这一项的系数 。
如下算法:
他的精确时间复杂度表示为: 2 * m + n ;
int keep(int m ,int n)
{
int count = 0;
for(int i = 0;i < 2*m ;i++)
{
count++;
}
for(int i = 0; i< n ;i++)
{
count++;
}
return count;
}
如果用大O表示法:(如果没有对m 和 n 的明确说明,则表示为O(m+n))
① m 与 n 相差不大
表示为:O(m) //2m+n可以近似为3m ,然后再将系数去掉
② m 远大于 n
表示为:O (m)
③m 远小于 n
表示为 O( n )
常用时间复杂度对比:
五、总结
1. 时间复杂度的用处是用来迅速判断一个算法的优劣,其运用的是底线思维,取得是最复杂时的情况。
2. 大O渐进表示法可以更加简明的表示时间复杂度,