1 时间复杂度
时间复杂度是衡量程序运行的快慢,对于一个程序来说,运行速度的快慢,有很多干扰因素,不同的硬件条件都可能影响到速度的快慢。时间复杂度来衡量,不是精确的衡量,只是单纯从代码本身衡量程序运行的效率来看,用算法基本操作执行的次数来作为时间复杂度。
而在实际操作中我们也不是精确的计算出执行的次数而是大概执行的次数,用到 “ 大O渐进表示法 ”。大O符号(Big O notation):是用于描述函数渐进行为的数学符号。
示例一
public static void func1(int N) {
int count = 0;
for (int i = 0; i < 10; i++) {
count++;
}
System.out.println(count);
}
时间复杂度为:O (1)
基本操作是count++。所谓 O (1) 不是指只执行了一次,而是指,无论问题规模 N多大,算法基本操作的执行次数是固定的。
示例二
public static void func2(int N) {
int count = 0;
for (int i = 0; i < 2 * N ; i++) {
count++;
}
int M = 10;
while ((M--) > 0) {
count++;
}
System.out.println(count);
}
时间复杂度为:O ( N )
这时注意,运行次数的函数只保留最高阶,把其他的都去掉,得到的结果就是大 O 阶。
示例三
public static 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++;
}
System.out.println(count);
}
时间复杂度为:O ( N + M )
问题有两个规模指标,M 和 N,for循环一个 M 次,一个N次。
示例四
public static int binarySearch(int[] arr,int toSearch) {
int left = 0;
int right = arr.length - 1;
while(left <= right){
int mid = (left + right) / 2;
if(toSearch > arr[mid]){
left = mid + 1;
}
else if(toSearch < arr[mid]) {
right = mid -1;
}else
System.out.println(mid);
}
return -1;
}
时间复杂度:O ( logN )
基础操作是比较,每比较一次,就会少一半的数据,数据由 N 变成 N/2 ,再变成N/4, N/8, … N/2^k,k是循环的次数,,所以时间复杂度近似为以二为底的 N 的对数。
示例五
public static void func4(int N,int M) {
int count = 0;
for (int i = 0; i < N ; i++) {
for (int j = 0; j < N ; j++) {
count++;
}
}
for (int i = 0; i < 2 * N ; i++) {
count++;
}
int M = 10;
while ((M--) > 0) {
count++;
}
S
System.out.println(count);
}
时间复杂度为:O ( N2 )
虽然有三处循环都涉及到,但是我们只保留最高阶,最高阶就是 2 ,就把其他的都去掉。
示例六
public static int factorial(int num) {
if(num < 2) {
return num;
}
return factorial(num - 1) * num;
}
假设 num 是5 ,求 5 的阶乘 :1x2x3x4x5 ,图中黄色表示递归进去的时候,红色是一层一层返回回来。
时间复杂度为:O ( N)
示例七
public static int fibonacci(int N) {
if (N <= 2) {
return 1;
}
return fibonacci(N - 1) + fibonacci(N - 2);
}
假设求斐波那契数列的第五个数,递归方法同上面的求阶乘。
时间复杂度:O ( 2N )
2 空间复杂度
空间复杂度,是对一个算法在运行过程中临时占用存储空间大小的量度,也同样用大O渐进表示法。和时间复杂度不同的是,要注意空间是可以重复利用的。
示例一:
public static void bubbleSort(int[] array) {
for (int end = array.length; end > 0; end--) {
boolean sorted = true;
for (int i = 1; i < end; i++) {
if (array[i - 1] > array[i]) {
Swap(array, i - 1, i);
sorted = false;
}
}
if (sorted == true) {
break;
}
}
}
空间复杂度:O ( 1 )
程序本身的空间不算在内,然后这之中创建的 end、sorted、i 等等,无论问题规模如何,它们都是每循环一遍就会清除掉然后重新创建,与问题规模无关,用到的空间是固定的,就是O ( 1 )。
示例二:
public static int[] fibonacci(int n) {
long[] fibArray = new long[n + 1];
fibArray[0] = 0;
fibArray[1] = 1;
for (int i = 2; i <= n ; i++) {
fibArray[i] = fibArray[i - 1] + fibArray[i - 2];
}
return fibArray;
}
空间复杂度:O ( N )
循环中的 i 的空间和问题规模没有关系,要计算的就是 long[n + 1] 这个数组的空间,而它的空间跟问题规模有关,问题规模多大增加,它就增加多大。
示例三:
public static int fibonacci(int N) {
if (N <= 2) {
return 1;
}
return fibonacci(N - 1) + fibonacci(N - 2);
}
空间复杂度:O ( N )
因为不停的出栈入栈,空间在不断重复利用,利用最多时,是 4 个栈帧,和问题规模相关,问题规模增大多少,它调用栈帧时,利用最多栈帧的数量就越多。