1. 时间复杂度
来衡量算法运行时间,随输入数据规模增长的变化趋势的一个指标。
具体来说,时间复杂度反映了算法执行所需时间的增长率,通常使用大O符号来表示。它可以帮助开发者理解算法的效率,以及在数据规模增大时,算法的性能表现。
以下是一些常见的时间复杂度类别:
- 常数阶(O(1)):无论输入数据的规模如何变化,算法的执行时间都保持不变。
- 对数阶(O(log n)):算法的执行时间与输入数据的对数成正比,通常出现在二分查找等算法中。
- 线性阶(O(n)):算法的执行时间与输入数据的规模成线性关系,即数据规模增加多少倍,执行时间也增加多少倍。
- 平方阶(O(n^2)):算法的执行时间与输入数据的规模的平方成正比,典型的是冒泡排序等简单排序算法。
- 立方阶(O(n^3)):算法的执行时间与输入数据的规模的立方成正比,如某些三维问题的求解算法。
- 指数阶(O(2^n)):算法的执行时间随着输入数据规模的增大呈指数级增长,如某些递归算法。
2. 空间复杂度
空间复杂度,简而言之,就是衡量算法在运行过程中临时占用的存储空间大小的一个重要指标。它不仅包括了为函数体中的局部变量所分配的存储空间,也涵盖了参数表中的形参变量所占用的空间。特别地,对于递归算法,其空间复杂度还需考虑递归调用过程中所使用的堆栈空间。
3. 示例
3.1. 冒泡排序
public class BubbleSort {
public static void bubbleSort(int[] arr) {
int n = arr.length;
for (int i = 0; i < n; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
// 交换 arr[j] 和 arr[j+1]
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
}
时间复杂度:仍然是O(n^2),因为有两层嵌套的循环,外层循环执行n次,内层循环执行n-i-1次。
空间复杂度:O(1),因为除了输入数组外,我们只使用了几个用于交换的临时变量。
3.2. 递归阶乘
public class Factorial {
public static long factorial(int n) {
if (n == 1) {
return 1;
} else {
return n * factorial(n - 1);
}
}
}
时间复杂度:O(n),因为递归的深度为n,每一层递归都是常数时间的操作。
空间复杂度:O(n),由于递归调用,Java的方法调用栈会随着递归深度而增长,栈的大小与n成正比。
3.3. 斐波那契数列
public class Fibonacci {
public static int fibonacci(int n) {
if (n <= 1) {
return n;
} else {
return fibonacci(n - 1) + fibonacci(n - 2);
}
}
}
时间复杂度:仍然是指数级的,因为对于每一个子问题fibonacci(n),都会递归地计算两个子问题fibonacci(n-1)和fibonacci(n-2),导致大量的重复计算。
空间复杂度:同样是指数级的,递归调用的深度随着n的增加而迅速增长,导致栈空间的使用量急剧增加。
对于斐波那契数列的计算,我们通常也会使用迭代或者带记忆化的递归(例如,使用一个数组来存储已经计算过的斐波那契数)来避免重复计算,从而提高效率。