算法基础-时间复杂度、对数器、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

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值