算法复杂度
1. 基础算法案例及复杂度分析
为了判断一种算法在解决某种问题时的效率,需要对算法进行分析,但是以下分析并不严谨,只是提供分析思路。
一些术语:
输入规模:输入的大小;
基本运算:在确定输入规模后,某条或某段指令是程序的核心,是算法完成的总体工作量与这条或这段指令的执行次数成正比。这条或这段指令即称为基本运算;
时间复杂度:针对每个输入规模,计算该算法执行了多少次基本运算的取值。(以下用T(n)来表示,输入规模为n时的时间复杂度);
平均情况时间复杂度:A(n)
1.1 顺序查找
/**
* 顺序查找
* 如查找不到则返回-1
* @param n 拥有n个键的数组
* @param s 键的数组
* @param 查找键
* @param location 查找键所在的位置
*/
public int seqsearch(int n,int s[],int x){
int location = 0;
while(location<n && s[location]!=x){
location++;
}
if(location>=n){
location =-1;
}
return location;
}
基本运算:while()循环,查找键值x
输入规模:
1.2 数组求和
/**
* 数组求和
*/
public void sum(int[] arr){
int total = 0;
for(int n=0;n<arr.length;n++){
total += arr[n];
}
}
基本运算:”for()”循环,将数组中的每个项目加到total中
输入规模:n,数组中的项目数
时间复杂度:T(n)=n,基本运算执行n次。
1.3 交换排序
/**
* 交换排序
* 输出非递减数组
* @param arr 排序的数组
*/
public int[] exchangesort(int[] arr){
for(int i=0;i<arr.length-1;i++){
for(int j=i+1;j<arr.length;j++){
if(arr[i]>arr[j]){
int n = arr[i];
arr[i] = arr[j];
arr[j] = n;
}
}
}
return arr;
}
基本运算:两个for()循环中的内容,比较arr[i]和arr[j]
输入规模:n,数组中的项目数
时间复杂度:for-i循环执行n-1次,每次for-j循环n-i-1次,具体分析见下表,所以
T(n)=(n−1)+(n−2)+(n−3)+......+1=(n−1)∗n/2
i | 起始 j | 执行次数 |
---|---|---|
0 | 1 | n-1 |
1 | 2 | n-2 |
2 | 3 | n-3 |
… | … | … |
n-2 | n-1 | 1 |
1.4 矩阵乘法
/**
* 矩阵乘法
* @param a 矩阵a
* @param b 矩阵b
* @return 返回a*b的矩阵
*/
public int[][] matrixmult(int[][] a,int[][] b){
if(a[0].length!=b.length){
throw new RuntimeException("两矩阵不满足相乘条件");
}
int row = a.length;
int col = b[0].length;
int arr[][] = new int[row][col];
for(int i=0;i<row;i++){
for(int j=0;j<col;j++){
for(int k=0;k<row;k++){
arr[i][j] += a[i][k]*b[k][j];
}
}
}
return arr;
}
基本运算:三个循环内的指令
输入规模:n,行列数
时间复杂度:假设改数组行列相等,都为n,则
T(n)=n∗n∗n=n3
1.5 二分查找
/**
* 二分查找
* 基于非递减的有序数组
* @param s 非递减数组,查找区域
* @param x 查找值
* @return 返回查找值所在位置,如查找不到返回-1
*/
public int binsearch(int[] s,int x){
int location =-1;
int start = 0;
int end = s.length-1;
int mid;
while(location==-1 && start<=end){
mid = (start+end)/2;
if(s[mid]==x){
location =mid;
}else if(s[mid]< x){
start = mid+1;
}else{
end = mid-1;
}
}
return location;
}
基本运算:三个循环内的指令
输入规模:n,行列数
时间复杂度:假设改数组行列相等,都为n,则
T(n)=n∗n∗n=n3
1.6确定斐波那契数列的第n项
A.方法一
/**
- 确定斐波那契序列的第n项
- @param n 序列的第n项
- @return 返回序列第n项的值
*/
int fib1(int n){
if(n<=1){
return n;
}
return fib1(n-1)+fib1(n-2);
}
B.方法二
/**
* 确定斐波那契序列的第n项
* @param n 序列的第n项
* @return 返回序列第n项的值
*/
public int fib2(int n){
int arr[] = new int[n];
arr[0]=0;
if(n>0){
arr[1]=1;
for(int i= 2;i<n;i++){
arr[i]=arr[i-1]+arr[i-2];
}
}
return arr[n-1];
}
两个方法对比:
n | n+1 | 2^n+1 | 方法一执行时间 | 方法二执行时间 |
---|---|---|---|---|
40 | 41 | 1.05E+06 | 1048us | 41ns |
60 | 61 | 1.07E+09 | 1s | 61ns |
80 | 81 | 1.10E+12 | 18min | 81ns |
100 | 101 | 1.13E+15 | 13day | 101ns |
120 | 121 | 1.15E+18 | 36year | 121ns |
160 | 161 | 1.21E+24 | 3.8*10^7年 | 161ns |
200 | 201 | 1.27E+30 | 4*10^13年 | 201ns |
2.阶
对于时间复杂度有 n,n2 等算法,无论两种算法需要多长时间来处理基本运算,当 n 值足够大时,第一种算法的效率总是高于第二种。
- 线性时间算法:
n,10n 等- 二次时间算法:
n2,0.01n2
等
- 纯二次函数: 5n2,5n2+10 等没有包含线性选项
- 完全二次函数: 5n2+n+20 等包含线性选项
2.1 θ 西塔
所有的二次函数的复杂度函数构成的集合,可以表示为
θ(n2) ,因为低阶项可以忽略。
例如:算法3的的 T(n)=n∗(n−1)/2∈θ(n2)复杂度类别
- θ(lgn)
- θ(n)
- θ(nlgn)
- θ(n2)
- θ(n3)
-
θ(2n)
2.1 大O(上限无限接近)
对于给定复杂度函数 f(n) , O(f(n)) 是由一些复杂度函数 g(n) 组成的集合,对于其中每个 g(n) ,必存在某一正实数 c 及某一非负整数
N ,使得对于所有 n>=N ,满足 g(n)<=c∗f(n)
如果$g(n)∈O(f(n)),就说g(n)是f(n)的大O
例如:
2.2 Ω 下限无限接近
对于一个给定的复杂度函数
f(n),o(f(n)) 是由所有满足以下条件的复杂度函数 g(n) 组成的集合;对于每个 g(n) ,必存在某一正常实数 c 及某一非负整数N ,使得对于所有 n>=N ,有 g(n)>=c∗f(n)
2.3 小o 上限无限接近
对于一个给定的复杂度函数 f(n),o(f(n)) 是由所有满足以下条件的复杂度函数 g(n) 组成的集合;对于每个正常实数 c ,必存在一个非负整数
N ,使得对于所有 n>=N ,有 g(n)<=c∗f(n)
阶的性质
以后补充
- 二次时间算法:
n2,0.01n2
等