衡量算法的好坏
1.正确性:算法应当满足具体问题的要求。
2.可读性:算法主要是为了人的阅读交流,其次才用于机器的执行。
3.健壮性:当输入非法数据时,算法也能适当地做出反应或进行处理,而不是产生莫名其妙的结果。
4.效率和低存储量需求:效率就是算法执行的时间。存储量需求是算法执行过程中需要的最大存储空间。
时间复杂度
T
(
n
)
=
O
(
f
(
n
)
)
T(n)=O(f(n))
T(n)=O(f(n))
一般情况下,算法中基本操作重复执行的次数是问题规模n的某个函数
f
(
n
)
f(n)
f(n),它会随着问题规模n的增大,算法执行时间的增长率和
f
(
n
)
f(n)
f(n)的增长率相同,称作时间复杂度。
时间复杂度的衡量
如果用时间量度来衡量时间复杂度,我们没法估计时间长短,只有用电脑亲测才可以知道具体花费的时间,但是没有必要每一个算法都要通过电脑运行,所以,我们采用一个算法中一条最基本操作,以这条最基本操作所执行的次数作为算法的时间量度。
时间复杂度
O
O
O渐进表示法
对于问题规模的函数
f
(
n
)
f(n)
f(n),只保留最高阶项,其他项省略,且最高项系数也要省略。
时间复杂度看最差情况
如下代码示例,在有N个元素的数组中查找一个元素是否存在,若所要查找的元素在第一个,时间复杂度为
O
(
1
)
O(1)
O(1),如果这个元素在最后一个或者不存在,时间复杂度就变为
O
(
n
)
O(n)
O(n),前者就是最优,后者为最差,平均就是
1
+
2
+
3
+
.
.
.
+
n
n
\frac{1+2+3+...+n}{n}
n1+2+3+...+n 近似为
n
2
\frac{n}{2}
2n 。
int find(int[] array, int n, int x) {
int i = 0;
int pos = -1;
for (; i < n; ++i) {
if (array[i] == x) {
pos = i;
break;
}
}
return pos;
}
二分查找的时间复杂度
二分查找:
假设对N个元素,第一次查找N ,第二次
N
2
\frac{N}{2}
2N,第三次
N
4
\frac{N}{4}
4N,后面依次是前一次的
1
2
\frac{1}{2}
21,直到剩下一个就查找一次。
反之,假设查找了m次,就可以得到
1
∗
2
∗
2
∗
⋯
∗
2
⎵
m
\underbrace{1*2*2*\cdots*2}_{m}
m
1∗2∗2∗⋯∗2 =
2
m
2^{m}
2m,所以有
2
m
2^{m}
2m=N,就可以得到
f
(
m
)
f(m)
f(m)=
f
(
l
o
g
2
N
)
f(log_{2}N)
f(log2N),即时间复杂度为
O
(
l
o
g
2
N
)
。
O(log_{2}N)。
O(log2N)。
int BinarySearch (int* arr,int n,int x){
int left = 0;
int right = n-1;
while (left <= right){
int mid = (left + right) / 2;
if (x > arr[mid]){
left = mid + 1;
}
else if (x < arr[mid]){
right = mid - 1;
}
else{
return 1;
break;
}
}
if (left>right){ //表示未找到,输出-1
return -1;
}
}
空间复杂度
S
(
n
)
=
O
(
f
(
n
)
)
S(n)=O(f(n))
S(n)=O(f(n))
空间复杂度是对一个算法在运行过程中临时占用存储空间大小的量度。它也是问题规模n的函数。一个算法的空间复杂度只考虑在运行过程中为局部变量分配的存储空间的大小,它包括为参数表中形参变量分配的存储空间和为在函数体中定义的局部变量分配的存储空间两个部分。若一个算法为递归算法,其空间复杂度为递归所使用的堆栈空间的大小,它等于一次调用所分配的临时存储空间的大小乘以被调用的次数(即为递归调用的次数加1,这个1表示开始进行的一次非递归调用)。
如何求空间复杂度? 普通函数&递归函数
一个算法的空间复杂度只考虑在运行过程中为局部变量分配的存储空间的大小,它包括为参数表中形参变量分配的存储空间和为在函数体中定义的局部变量分配的存储空间两个部分。若一个算法为递归算法,其空间复杂度为递归所使用的堆栈空间的大小,它等于一次调用所分配的临时存储空间的大小乘以被调用的次数(即为递归调用的次数加1,这个1表示开始进行的一次非递归调用)。
递归的空间复杂度:每次递归所开空间深度。
递归斐波那契数列的:时间、空间复杂度,并对其进行优化,伪递归优化—>循环优化
时间复杂度,空间复杂度均为
O
(
n
)
O(n)
O(n)的递归斐波拉契数列
long long fib(int n){
if (n == 1 || n == 2)
return 1;
else
return fib(n - 1) + fib(n - 2);
}
时间复杂度 O ( n ) O(n) O(n),空间复杂度均为 O ( n 2 ) O(n^{2}) O(n2)的递归斐波拉契数列
long long fib(long long first,long long second,int n){ //伪递归
if (n <3)
return 1;
if (n==3)
return first+second;
return fib(second,first+second,n-1);
}
我们可以用for循环代替这个伪递归,也可以达到同样的目的
int long long fib(long long first,long long second,int n){
int first = 1;
int second = 1;
int sum=0;
if (n == 1||n == 2)
return 1;
if (n >= 3){
for (int i = 2; i < n; i++){
sum = first + second;
first = second;
second = sum;
}
return sum;
}
}
常见时间复杂度
常见的有:常量阶
O
(
1
)
O(1)
O(1), 线性阶
O
(
n
)
O(n)
O(n),平方阶
O
(
n
2
)
O(n^{2})
O(n2), 对数阶
O
(
l
o
g
n
)
O(log n)
O(logn), 指数阶
O
(
2
n
)
O(2^{n})
O(2n)。