一、算法效率
算法的复杂度
算法在编写成可执行程序后,运行时需要耗费时间资源和空间(内存)资源 。因此衡量一个算法的好坏,一般 是从时间和空间两个维度来衡量的,即时间复杂度和空间复杂度。
时间复杂度主要衡量一个算法的运行快慢,而空间复杂度主要衡量一个算法运行所需要的额外空间。在计算 机发展的早期,计算机的存储容量很小。所以对空间复杂度很是在乎。但是经过计算机行业的迅速发展,计 算机的存储容量已经达到了很高的程度。所以我们如今已经不需要再特别关注一个算法的空间复杂度。
现如今,算法的时间复杂度的重要性要高于空间复杂度,我们在使用一个软件的时候都希望它的运行得快速不卡顿,不是很在意这个软件运行占用的空间,因为现在手机电脑更新迭代很快,里面的内存也越来越高,低内存的设备也越来越少,人们在软件使用过程中,基本不用考虑内存不够用的问题
这就要从这个人说起了,他是英特尔(Intel)创始人之一戈登·摩尔(Gordon Moore)。
他提出了摩尔定律:当价格不变时,集成电路上可容纳的晶体管数目,约每隔18个月便会增加一倍,性能也将提升一倍。换言之,每一美元所能买到的电脑性能,将每隔18个月翻两倍以上。这一定律揭示了信息技术进步的速度。
二、时间复杂度
1、时间复杂度的概念
时间复杂度的定义:在计算机科学中,算法的时间复杂度是一个函数,它定量描述了该算法的运行时间。一 个算法执行所耗费的时间,从理论上说,是不能算出来的,只有你把你的程序放在机器上跑起来,才能知 道。但是我们需要每个算法都上机测试吗?是可以都上机测试,但是这很烦,所以才有了时间复杂度这个 分析方式。一个算法所花费的时间与其中语句的执行次数成正比例,算法中的基本操作的执行次数,为算法的时间复杂度。
2、大O的渐进表示法
大O符号(Big O notation):是用于描述函数渐进行为的数学符号。
3、常见时间复杂度计算举例
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 * N + 10
由大O的渐进表示法得该代码的时间复杂度为 O(N²)
例二 、 计算Func2的时间复杂度?
void Func2(int N)
{
int count = 0;
for (int k = 0; k < 2 * N; ++k)
{
++count;
}
int M = 10;
while (M--)
{
++count;
}
printf("%d\n", count);
}
函数表达式
F(N) = 2 * N + 10
由大O的渐进表示法得该代码的时间复杂度为 O(N)
例三 、 计算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);
}
函数表达式
F(N) = M + N
有三种情况
这段代码的时间复杂度就没有固定答案,只有具体情况具体分析
例四 、 计算Func4的时间复杂度?
void Func4(int N)
{
int count = 0;
for (int k = 0; k < 100; ++k)
{
++count;
}
printf("%d\n", count);
}
函数表达式
F(N) = 100
100是常数
那么由大O的渐进表示法得该代码的时间复杂度为 O(1),代表常数次
例五 、 计算strchr的时间复杂度?
遍历的次数是取决于str的长度,那么时间复杂度为O(1)
const char* strchr(const char* str, int character);
例六 、 计算BubbleSort的时间复杂度?
void BubbleSort(int* a, int n)
{
assert(a);
for (size_t end = n; end > 0; --end)
{
int exchange = 0;
for (size_t i = 1; i < end; ++i)
{
if (a[i - 1] > a[i])
{
Swap(&a[i - 1], &a[i]);
exchange = 1;
}
}
if (exchange == 0)
break;
}
}
按照前面几个例子的规律,时间复杂度为O(N),但是发现F(N) = N * (N - 1) / 2
时间复杂度为O(N²)
例七 、 计算BinarySearch的时间复杂度?
int BinarySearch(int* a, int n, int x)
{
assert(a);
int begin = 0;
int end = n - 1;
while (begin < end)
{
int mid = begin + ((end - begin) >> 1);
if (a[mid] < x)
begin = mid + 1;
else if (a[mid] > x)
end = mid;
else
return mid;
}
return -1;
}
三、空间复杂度
注意:函数运行时所需要的栈空间(存储参数、局部变量、一些寄存器信息等)在编译期间已经确定好了,因 此空间复杂度主要通过函数在运行时候显式申请的额外空间来确定。
四.、常见复杂度对比
一般算法常见的复杂度如下:
五、 复杂度的oj练习
3.1消失的数字OJ链接:https://leetcode-cn.com/problems/missing-number-lcci/
如果不考虑复杂度问题,就可以有这种方法,直接动态开辟空间
思路一 排序+遍历
下一个数不等于下一个数据就+1,这个下一个数字就是消失的数字
时间复杂度 O(log(N*N))
//思路一 动态开辟空间
int Findnum(int* pc,int sz)
{
assert(pc);
int* arr2;
arr2 = (int*)malloc((sz+1)*sizeof(int));
if (arr2 == NULL)
{
perror("Findnum::malloc");
return 0;
}
memset(arr2, -1, (sz+1)*sizeof(int));
int i = 0;
for (i = 0;i < sz;i++)
{
int tmp = *(pc + i);
*(arr2+tmp) = *(pc + i);
}
int j = 0;
for (j = 0;j < sz + 1;j++)
{
if (*(arr2+j) != j)
{
return j;
}
}
free(arr2);
arr2 = NULL;
}
int main()
{
int arr1[] = { 0,3,5,6,1,2 };
int sz = sizeof(arr1) / sizeof(arr1[0]);
int num = Findnum(arr1,sz);
printf("%d", num);
return 0;
}
思路二 异或
时间复杂度O(N)
//思路二 异或
int Findnum(int* pc,int sz)
{
assert(pc);
int i = 0;
int n = 0;
for (i = 0;i < sz;i++)
{
n ^= *(pc + i);
}
for (int j = 0;j < sz + 1;j++)
{
n^=j;
}
return n;
}
int main()
{
int arr1[] = { 0,3,5,6,1,2 };
int sz = sizeof(arr1) / sizeof(arr1[0]);
int num = Findnum(arr1,sz);
printf("%d", num);
return 0;
}
思路三 求和
0+N等差数列公式计算结果就是消失的数字
时间复杂度O(N)
//思路三 求和
int main()
{
int arr1[] = { 0,3,5,6,1,2 };
int sum1 = 0;
int sum2 = 0;
int sz = sizeof(arr1) / sizeof(arr1[0]);
int i = 0;
for (i = 0;i < sz;i++)
{
sum1 = sum1+arr1[i];
}
int j = 0;
for (j = 0;j < sz + 1;j++)
{
sum2 = sum2 + j;
}
int n = sum2 - sum1;
printf("%d", n);
return 0;
}