算法基础-时间复杂度、对数器、Master定理剖析

算法基础

算法的复杂度

算法复杂度(算法复杂性)是用来衡量算法运行所需要的计算机资源(时间、空间)的量。通常我们利用渐进性态来描述算法的复杂度。

用n表示问题的规模,T(n)表示某个给定算法的复杂度。所谓渐进性态就是令n→∞时,T(n)中增长最快的那部分。

比如:T(n) = 2 * n ^ 2 + n log n + 3,那么显然它的渐进性态是 2 * n ^ 2,因为当n→∞时,后两项的增长速度要慢的多,可以忽略掉。

在算法复杂度分析中,log通常表示以2为底的对数。

引入渐进性态是为了简化算法复杂度的表达式,只考虑其中的主要因素。

当比较两个算法复杂度的时候,如果他们的渐进复杂度的阶不相同,那只需要比较彼此的阶(忽略常数系数)就可以了。

总之,分析算法复杂度的时候,并不用严格演算出一个具体的公式,而是只需要分析当问题规模充分大的时候,复杂度在渐进意义下的阶。

记号O给出了函数f(n)在渐进意义下的上界(但不一定是最小的)。

认识时间复杂度

  1. 常数操作
  2. 时间复杂度
  3. 一个简单的理解时间复杂度的例子

常数时间的操作:

  • 一个操作如果和数据量没有关系,每次都是固定时间内完成的操作,叫做常数操作。

时间复杂度:

  • 为一个算法流程中,常数操作数量的指标。常用O(读作big O)来表示。
  • 具体来说:在常数操作数量的表达式中,只要高阶项,不要低阶项,也不要高阶项的系数,剩下的部分如果记为f(N),那么时间复杂度为O(f(N))。

评价一个算法流程的好坏,先看时间复杂度的指标,然后再分析不同数据样本下的实际运行时间,也就是常数项时间。

一个简单的理解时间复杂度的例子:

一个有序数组A, 另一个无序数组B, 请打印B中的所有不在A中的数, A数组长度为N, B数组长度为M。

算法流程1:

  • 对于数组B中的每一个数, 都在A中通过遍历的方式找一下;
  • 算法时间复杂度分析:时间复杂度为O(N * M);

算法流程2:

  • 对于数组B中的每一个数, 都在A中通过二分的方式找一下;
  • 算法时间复杂度分析:时间复杂度为O(logN * M);
    • 有M个数,每个数进行二分查找时间复杂度为O(logN);

算法流程3:

  • 先把数组B排序, 然后用类似外排的方式打印所有在A中出现的数;
  • 算法时间复杂度分析:时间复杂度为O((M * logM) + (M + N));
    • 无序数组B排序的时间复杂度假定为O(M * logM)
    • 外排时间复杂度为两个数组元素的长度和O(M + N) ;

对于同一个问题,比较以上3种不同的算法实现的时间复杂度情况:

  • 算法流程1:时间复杂度为 O(N*M) , 性能最差;
  • 算法流程2:时间复杂度为 O(logN * M) ;
  • 算法流程3:时间度杂度为 O(M * logM) + O(M+N) ;
  • 比较算法流程2和算法流程3,因为存在两个变量,M和N,需要根据不同的数据样本量具体分析,采用渐进性分析:
    • 当 M 远大于 N 时, N 可看作常数项忽略:
      • 算法流程2: O(logN * M) ,得到时间复杂度为: O(M)
      • 算法流程3: O(M * logM) + O(M + N) ,得到时间复杂度为: O(M * logM)
      • 得出算法流程2 优于 算法流程3;
    • 当 N 远大于 M 时:
      • 算法流程2: O(logN * M)
      • 算法流程3: O(M * logM) + O(M + N)
      • 需要根据 M 和 N 的具体的样本量才能分析

对数器

对数器的概念和使用:

  1. 有一个你想要测的方法a;
  2. 实现一个绝对正确但是复杂度不好的方法b;
  3. 实现一个随机样本产生器;
  4. 实现比对的方法;
  5. 把方法a和方法b比对很多次(100000+)来验证方法a是否正确。
  6. 如果有一个样本使得比对出错, 打印样本分析是哪个方法出错;
  7. 当样本数量很多时比对测试依然正确,可以确定方法a已经正确。

注意要点:

  • 要测试的算法a是时间复杂度比较低的算法,而算法b唯一要求就是保证正确,而不用管复杂度的高低
  • 随机产生的样本大小要小,这里说的是样本的大小而不是样本的个数。因为出错时,小样本方便分析。
  • 随机产生的样本个数要多,100000+ - 只要大量随机产生的样本才可能覆盖所有的情况。
  • 如果算法b也无法保证完全的正确,在不断出错调试的过程中,也可以不断完善b,最终达到a和b都正确的完美结果。

对数器的使用:

  • 在算法竞赛或代码笔试中,要提前准备好常用算法的对数器,快速编写完算法实现后,使用相应的算法对数器快速验证算法的正确性,减少扣分或扣时;
  • 平常在手写算法实现时,验证算法的正确性非常有效;

AlgorithmComparator:

  • int[] generateRandomArray(int maxSize, int maxValue) 随机样本产生器
  • void comparator(int[] arr) 实现绝对正确的方法b
  • boolean isEqual(int[] arr1, int[] arr2) 实现比对方法a和方法b的处理结果的方法
  • void printArray(int[] arr) 比对失败时打印样本分析

Master定理

有些算法在处理一个较大规模的问题时,往往会把问题拆分成几个子问题,对其中的一个或多个问题递归地处理,并在分治之前或之后进行一些预处理、汇总处理。

这时候我们可以得到关于这个算法复杂度的一个递推方程,通过 Master公式 求解此方程便能得到算法的复杂度。

Master公式:T(N) = a * T(N/b) + O(N^d)

  • 说明:
    • T(N):样本量为N的情况下的时间复杂度
    • a*T(N/b) : 子过程调用的时间复杂度,N/b指子过程对数据的拆分情况,如二分为 N/2;
    • O(N^d) :除掉子过程调用外,剩余操作的时间复杂度
  • log(b,a) > d ,时间复杂度为:O(N^log(b,a))
  • log(b,a) = d ,时间复杂度为:O(N^d * logN)
  • log(b,a) < d ,时间复杂度为:O(N^d)

Master公式适用范围:划分为子过程的样本量规模是一致的。

当划分的子过程的样本量不一致时,不能使用Master公式,例:T(N) = T(2 * N / 5) + T(3 * N / 5) + O(N^2)

递归案例:在一个数组中找最大值

  • 使用递归方式:
    • 将数组切分成两部分,分别找到左边部分的最大值和右边部分的最大值,返回两数中的最大值;
    • 递归终止条件:当L == R,当左边界等于右边界时,只有一个数,直接返回;
  • 递归实现时间复杂度分析:T(N) = 2 * T(N/2) + O(1)
    • 样本量等分为两部分,每部分的时间复杂度为T(N/2)
    • 比较大小操作的时间复杂度为:O(1)
    • 代入Master公式得:T(N) = 2 * T(N/2) + O(N^0), a=2,b=2,d=0
    • 符合公式 log(2,2) = 1 > 0 , 得时间复杂度为:O(N^log(2,2)) -> O(N)
public static int findMax(int[] arr,int L, int R){
    if(L == R){
        return arr[L];
    }

    int mid = (L+R)/2;
    int leftMax = findMax(arr, L, mid);
    int rightMax = findMax(arr, mid+1 , R);
    return leftMax > rightMax ? leftMax : rightMax;
}

案例2:如果某个算法时间复杂度公式为:T(N) = 2 * T(N/2) + O(N):

  • 将样本量为N等分为两部分,每部分时间复杂度为:T(N/2)
  • 计算结果操作的时间复杂度为:O(N)
  • 代入Master公式得:T(N) = 2 * T(N/2) + O(N^1), a=2,b=2,d=1
  • 符合公式 log(2,2) = 1 等于 1 , 得时间复杂度为:O(N^1 * logN) -> O(N * logN)

相关链接

gitee地址:https://gitee.com/chentian114/chen_algorithm_study

github地址:https://github.com/chentian114/chen_algorithm_study

CSDN地址:https://blog.csdn.net/chentian114/category_10828595.html

公众号

知行chen

参考

Leetcode

左程云 牛客网 算法初级班课程

算法时间复杂度是衡量算法执行时间与输入数据量之间关系的一个量度。它通常用大O符号(O-notation)来表示,用于描述算法运行时间的增长率。以下是一些基本概念和计算方法: 1. 基本操作:在算法中,执行时间主要由基本操作的执行次数决定,基本操作通常是算法中最简单的计算步骤。 2. 最坏情况分析:时间复杂度通常针对最坏的情况来分析,即输入数据最不利于算法性能的场景。 3. 大O表示法:大O符号用于表示上界,它忽略低阶项和常数系数。例如,如果一个算法的执行次数为3n² + 2n + 1,其时间复杂度表示为O(n²)。 4. 常见的时间复杂度: - O(1):常数时间复杂度,表示算法的执行时间不随输入数据的大小变化而变化。 - O(log n):对数时间复杂度,常见于使用二分查找的算法。 - O(n):线性时间复杂度,表示算法执行时间与输入数据量成正比。 - O(n log n):线性对数时间复杂度,常见于一些高效的排序算法,如快速排序和归并排序。 - O(n²):平方时间复杂度,常见于简单的嵌套循环结构。 - O(2^n):指数时间复杂度,这类算法随着数据量的增加而迅速变得非常慢。 5. 主定理Master Theorem):用于解决递归式的时间复杂度计算问题,适用于分治算法中的递归运行时间分析。 计算时间复杂度时,需要确定算法中的主导循环,并分析该循环中基本操作的执行次数如何随输入数据量n的增长而增长。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值