时间复杂度与空间复杂度计算方法介绍

时间复杂度(Time Complexity)

基本概念

  • 时间复杂度衡量的是算法执行所需的时间,它通常表示为输入数据的规模 ( n ) 的函数。

    一个算法执行所耗费的时间,从理论上说是不能算出来的,因为只有当把程序放在机器上跑起来,才能知道具体时间。但是需要每个算法都上机测试吗?是可以都上机测试,但是这很麻烦,所以才有了时间复杂度这个分析方式。

    一个算法所花费的时间与其中语句的执行次数成正比例,算法中的基本操作的执行次数,为算法的时间复杂度。

  • 大 O 符号(Big O Notation):用来描述算法的时间复杂度的上界,表示最坏情况下的时间增长趋势。

  • 常见的时间复杂度:

    • O(1) :常数时间复杂度,不随输入规模变化。eg:O(100) -> O(1)
    • O(log n) :对数时间复杂度,常见于二分查找。
    • O(n) :线性时间复杂度,常见于简单遍历算法。
    • O(n log n) :线性对数时间复杂度,常见于快速排序、归并排序。
    • O(n²) :平方时间复杂度,常见于嵌套循环。
    • O(2ⁿ) :指数时间复杂度,常见于递归的组合问题。
    • O(n!) :阶乘时间复杂度,常见于排列问题。
  • 计算方法

    • 逐步分析法:分析算法的每一行代码,累加时间复杂度。

      • 赋值语句、算术运算、比较运算等基本操作的时间复杂度都是 O(1)
      • 对于循环结构,如果循环次数是 n ,那么该部分的时间复杂度是 O(n)
      • 对于嵌套循环,如果内外层分别有 n 次和 m 次,那么时间复杂度是 O(n^m)
    • 取最大量级:最终时间复杂度为各部分时间复杂度的最大值。忽略系数和低阶项。

      • 忽略低阶项:eg:O(N^2+N) -> O(N^2)

      • 忽略系数:eg:O(2N^2) -> O(N^2)

        如果最高阶项存在且不是1,则去除与这个项目相乘的常数


示例

  • 常数时间复杂度 O(1)

    int a = 10;
    int b = a + 5;
    

    说明:无论输入数据的规模如何,上述代码中的每个操作都只需要常数时间。因此,这段代码的时间复杂度是 O(1)

  • 线性时间复杂度 O(n)

    示例1:

    for (int i = 0; i < n; i++) {
    	// O(1) 操作
    }
    

    说明:这里有一个简单的循环,循环执行了 n 次,每次循环执行 O(1) 的操作。因此,总时间复杂度是 O(n)

    示例2:

    for (int i = 0; i < n; i++) {
        for (int j = i; j < n; j++) {
            // O(1) 操作
        }
    }
    

    说明:在这个例子中,循环运行了 n 次,每次加法操作的时间复杂度是 O(1)。因此,总时间复杂度是 O(n) 。

  • 平方时间复杂度 O(n²)

    示例1:

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            // O(1) 操作
        }
    }
    

    说明:这是一个双重嵌套循环。外层循环执行 n 次,内层循环也执行 n 次,每次内层循环的操作时间复杂度是 O(1)。因此,总时间复杂度是 O(n×n) = O(n²)

    示例2:

    for (int i = 0; i < n; i++) {
        for (int j = i; j < n; j++) {
            // O(1) 操作
        }
    }
    

    说明:在这个例子中,内层循环的次数随外层循环的次数变化,第一次内层循环执行 n 次,第二次执行 n−1 次,以此类推,总时间复杂度为 O(n(n+1)/2) ,简化后依然为 O(n²)

  • 对数时间复杂度 O(log n)

    示例:

    int binarySearch(int arr[], int n, int target) {
        int low = 0;
        int high = n - 1;
        while (low <= high) {
            int mid = low + (high - low) / 2;
            if (arr[mid] == target)
                return mid;
            else if (arr[mid] < target)
                low = mid + 1;
            else
                high = mid - 1;
        }
        return -1;
    }
    

    说明:这是二分查找算法的实现 。在每次循环中,搜索区间都减半,因此时间复杂度为 O(log n)

  • 线性对数时间复杂度 O(n log n)

    示例:

    void mergeSort(int arr[], int l, int r) {
        if (l < r) {
            int m = l + (r - l) / 2;
            mergeSort(arr, l, m);
            mergeSort(arr, m + 1, r);
            merge(arr, l, m, r);
        }
    }
    

    说明:归并排序是一种典型的 O(n log n) 时间复杂度算法。它将数组递归地分成两半,直到每个子数组只剩一个元素,然后再合并这些子数组。每次合并的复杂度是 O(n) ,递归深度是 O(log n) ,因此总时间复杂度为 O(n log n)

  • 指数时间复杂度 O(2ⁿ)

    示例:

    int fibonacci(int n) {
        if (n <= 1)
            return n;
        else
            return fibonacci(n-1) + fibonacci(n-2);
    }
    

    说明:这里是递归计算斐波那契数列的代码。每次递归调用都需要计算两个子问题,导致时间复杂度为 O(2ⁿ) 。这种复杂度通常会导致非常长的执行时间,不适用于大规模数据。


空间复杂度(Space Complexity)

基本概念

  • 空间复杂度衡量的是算法执行过程中所需的存储空间,它表示为输入数据规模 ( n ) 的函数。
  • 大O符号:同样使用大O符号表示,表示所需空间的增长趋势。
  • 常见的空间复杂度:
    • O(1) :常数空间复杂度,表示不随输入规模变化的额外空间需求。
    • O(n) :线性空间复杂度,表示需要与输入规模成正比的空间。
    • O(n²) :平方空间复杂度,表示需要与输入规模平方成正比的空间。
  • 计算方法
    • 逐步分析法:分析算法中所需的存储空间,累加所有部分的空间复杂度。
    • 简单变量(如整型、浮点型)占用的空间是 ( O(1) )。
    • 数组、列表等数据结构占用的空间与其长度有关,例如一个长度为 ( n ) 的数组占用的空间是 ( O(n) )。
      递归调用时,需要考虑调用栈的空间开销。

示例

  • 常数空间复杂度 O(1)

    示例:

    int a = 5;
    int b = 10;
    int c = a + b;
    

    说明:无论输入数据的规模如何,这段代码只占用了固定的内存空间。因此,空间复杂度是 O(1)

  • 线性空间复杂度 O(n)

    示例:

    int arr[n];
    

    说明:如果声明了一个长度为 n 的数组,那么空间复杂度是 O(n) ,因为数组的大小随 n 的增加而增加

  • 平方空间复杂度 O(n²)

    示例:

    int matrix[n][n];
    

    说明:这里声明了一个 n×n 的矩阵,因此空间复杂度为O(n²) ,因为存储该矩阵所需的空间随 n 的平方增长

  • 指数空间复杂度 O(2ⁿ)

    示例:

    void allSubsets(int arr[], int n) {
        int subset_count = pow(2, n);
        for (int i = 0; i < subset_count; i++) {
            for (int j = 0; j < n; j++) {
                if (i & (1 << j))
                    printf("%d ", arr[j]);
            }
            printf("\n");
        }
    }
    

    说明:生成一个集合的所有子集需要存储 2ⁿ 个子集。每个子集都可能占用额外的存储空间,导致总体空间复杂度为 O(2ⁿ)


参考

评论 113
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墨鸦_Cormorant

大家喜欢的话可以点个关注投币哟

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值