复杂度分析

复杂度

复杂度分析是数据结构与算法的核心精髓,指在不依赖硬件、宿主环境、数据集的情况下,粗略推导,考究出算法的效率和资源消耗情况, 包括时间复杂度和空间复杂度

时间复杂度

首先从CPU的角度看待程序,每行代码执行的操作都包括: 读程序写程序运算。这里粗略估计,忽略每行代码读程序和写程序的时间
假设 每行代码执行(运算)的时间都是一样的,为 单位时间(即假设程序执行一次均消耗单位时间)

大 (O) 记号

[T(n)=O(f(n)) ]

其中:

  • (T(n)): 程序执行总时间
  • (n): 数据规模大小
  • (f(n)): 程序执行的总次数
  • (O):程序执行总时间(T(n))与(f(n))成正比

大(O)记号按最坏的情况来估计程序执行时间;大(\Omega)记号按最好情况估计程序执行时间;大(\Theta)记号按平均情况来估计程序执行时间。后文只分析大(O)记号。

复杂度分析

大(O)记号的性质:

  • 对任意常数(c>0),(O(f(n))=O(cf(n)))
  • 对任意常数(a>b>0),(O(n^a+n^b)=O(n^a))

常见时间复杂度

  1. 常数阶(O(1))
public void sum(int n) {
    int sum = 0; // 执行一次
    sum = n*2; // 执行一次
    System.out.println(sum); // 执行一次
}

程序执行常数次,与问题规模(n)无关,复杂度记为(O(1))

  1. 对数阶(O(logn))
public void logarithm(int n) {
    int count = 1; // 执行一次
    while (count <= n) { 执行logn次 count="count*2;" } < code></=>

该段代码什么时候会停止执行呢?是当count大于n时。也就是说多少个2相乘后其结果值会大于(n),即(2^x=n)。由(2^x=n)可以得到(x=logn),所以这段代码时间复杂度是(O(logn))。

  1. 线性阶(O(n))
public void circle(int n) {
    for(int i = 0; i < n; i++) { // &#x6267;&#x884C;n&#x6B21;
        System.out.println(i); // &#x6267;&#x884C;n&#x6B21;
    }
}
  1. 线性对数阶(O(nlogn))
    线性对数阶(O(nlogn))就是将一段时间复杂度为(O(logn))的代码执行(n)次
public void logarithm(int n) {
    int count = 1;
    for(int i = 0; i < n; i++) { // &#x6267;&#x884C;n&#x6B21;
        while (count <= n) { 执行logn次 count="count*2;" 执行nlogn次 } < code></=>
  1. 平方阶(O(n^2))
    双重for循环
public void square(int n) {
    for(int i = 0; i < n; i++){ // &#x6267;&#x884C;n&#x6B21;
        for(int j = 0; j <n; j++) { 执行n次 system.out.println(i+j); 执行n方次 } < code></n;>

复杂度分析

复杂度分析

示例如下(限定条件: (0

 public int Function(int n, int x)
 {
    int sum = 0;
    for (int i = 1; i <= n; ++i) { if (i="=" x) break; sum +="i;" } return sum; < code></=>
  • 当(x>n)时,此代码的时间复杂度是(O(n))
  • 当(1
  • 当(x=1)时,时间复杂度是(O(1))

这段代码在不同情况下,其时间复杂度是不一样的。所以为了描述代码在不同情况下的不同时间复杂度,我们引入了最好、最坏、平均时间复杂度。

  1. 最好情况时间复杂度(best case)

最好情况时间复杂度,表示在最理想的情况下,执行这段代码的时间复杂度。
上述示例就是当x=1的时候,循环的第一个判断就跳出,这个时候对应的时间复杂度就是最好情况时间复杂度。

  1. 最坏情况时间复杂度(worst case)

最坏情况时间复杂度,表示在最糟糕的情况下,执行这段代码的时间复杂度。
上述示例就是(n

  1. 平均情况时间复杂度(average case)

好和最好情况是极端情况,发生的概率并不大。为了更有效的表示平均情况下的时间复杂度,引入另一个概念: 平均情况时间复杂度
分析上面的示例代码,判断x在循环中出现的位置,有(n+1)种情况:(1

[\frac{((1+2+3+\cdots+n)+n)}{(n+1)}=\frac{n(n+3)}{2(n+1)} ]

大(O)表示法会省略系数、低阶、常量,所以平均情况时间复杂度是(O(n))。

考虑概率的平均情况复杂度:

(x)要么在(1\sim n)中,要么不在(1\sim n)中,所以它们的概率都是(1/2)。同时数据在(1\sim n)中各个位置的概率都是一样的为1/n。根据概率乘法法则,(x)在(1\sim n)中任意位置的概率是(1/(2n))。因此在前面推导过程的基础上,我们把每种情况发生的概率考虑进去,得到 考虑概率的平均情况复杂度

[1\dfrac{1}{2n}+2\dfrac{1}{2n}+3\dfrac{1}{2n}+\cdots+n\dfrac{1}{2n})+n\dfrac{1}{n}=\frac{3n+1}{4} ]

引入概率之后,平均复杂度变为(O(\frac{3n+1}{4})),忽略系数及常量后,最终得到加权平均时间复杂度(O(n))。

多数情况下,我们 不需要区分最好、最坏、平均情况时间复杂度。只有同一块代码在不同情况下时间复杂度有量级差距,我们才会区分3种情况,为的是更有效的描述代码的时间复杂度。

  1. 均摊情况时间复杂度

均摊复杂度是一个更加高级的概念,它是一种特殊的情况,应用的场景也更加特殊和有限。对应的分析方式称为: &#x644A;&#x8FD8;&#x5206;&#x6790;&#x5E73;&#x644A;&#x5206;&#x6790;
示例如下(限定条件:(0\leq x\leq n)且(0\leq n)且(n), (x)为整数):

int n;
public int Function2(int x)
{
    int count = 0;
    if (n == x)
    {
        for (int i = 0; i < n; i++)
            count += i;
    }
    else
        count = x;
    return count;
 }

共有(n+1)种情况,每种情况的概率均为(\dfrac{1}{n+1})当(0\leq x

[(1+1+\cdots+1)\dfrac{1}{n+1}+n\dfrac{1}{n+1}=\dfrac{2n}{n+1} ]

当省略系数及常量后,平均时间复杂度为(O(1))。
通过分析可以看出,上述示例代码复杂度遵循一定的规律,对应1个(O(n)),和n个(O(1))。针对这样一种特殊场景使用更简单的分析方法: 摊还分析法, 即把耗时多的复杂度均摊到耗时低的复杂度,得到对应的均摊时间复杂度为O(1)。

  • 均摊时间复杂度是将高高复杂度均摊到其余低复杂度,故一般均摊时间复杂度就等于最好情况时间复杂度。
  • 均摊时间复杂度是一种特殊的平均复杂度(特殊应用场景下使用)

空间复杂度

空间复杂度定性地描述该算法或程序运行所需要的存储空间大小,算法空间复杂度的计算公式记作:(S(n)=O(f(n))),其中(n)为问题的规模,(f(n))为语句关于(n)所占存储空间的函数。

参考:

Original: https://www.cnblogs.com/CJuncheng/p/16524542.html
Author: CJuncheng
Title: 复杂度分析

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/711399/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Timer

    public class Timer1 { private final TaskQueue queue = new TaskQueue();//这是&#…

    技术杂谈 2023年5月31日
    087
  • 实现Kubernetes可观测性的3个最佳工具

    一个管理和实施得当的可观测性系统为DevOps提供了细化的洞察力,可用于调试和治愈复杂系统。可观察性将监控、警报和日志与指标可视化及其分析相结合。它允许开发团队详细了解Kubern…

    技术杂谈 2023年6月1日
    0104
  • laravel中间件Middleware原理解析及实例

    laravel中间件Middleware原理解析及实例 一、总结 一句话总结: 二、laravel中间件Middleware原理解析 1、身份中间件 实例 legend3/app/…

    技术杂谈 2023年5月30日
    085
  • Day1 使用MarkDown

    二级标题 三级标题 Hello,world Hello,world Hello,world Hello,world java 分割线 超链接 A B VC https://imag…

    技术杂谈 2023年7月10日
    075
  • SQL查询语句–统计

    — 1、日统计查询填补 i->为时间差的天数 2022-05-10为终止时间 SET @i :=- 1; SELECT date_format( DATE_SUB( ’20…

    技术杂谈 2023年6月21日
    092
  • Fortify 代码扫描安装使用教程

    前言 Fortify 能够提供静态和动态应用程序安全测试技术,以及运行时应用程序监控和保护功能。为实现高效安全监测,Fortify具有源代码安全分析,可精准定位漏洞产生的路径,以及…

    技术杂谈 2023年5月31日
    0105
  • 告别输入密码,SSH记住密码和设置别名

    SSH记住密码是一件十分简单的事情,只是互联网上很多文章都误导了大家。下面这些命令有很多的option,想要了解更多的可以去Google查找。 在终端运行如下命令进行ssh的秘钥生…

    技术杂谈 2023年6月21日
    097
  • 应用程序架构指导袖珍版

    微软模式与实践小组最近发布了应用程序架构指导袖珍版本,总共有6本,分别介绍了不同类型应用程序的架构指导,包括敏捷架构方法、Mobile应用程序、RIA应用程序、富客户端应用程序、W…

    技术杂谈 2023年5月31日
    098
  • 使用Xamarin开发手机聊天程序 — 基础篇(大量图文讲解 step by step,附源码下载)

    如果是.NET开发人员,想学习手机应用开发(Android和iOS),Xamarin 无疑是最好的选择,编写一次,即可发布到Android和iOS平台,真是利器中的利器啊!而且,X…

    技术杂谈 2023年6月1日
    0111
  • Fetch:下一代 Ajax 技术

    Ajax,2005年诞生的技术,至今已持续了 10 年。它是一种在客户端创建一个异步请求的技术,本质上它不算创新,是一组技术的组合。它的核心对象是 XMLHttpRequest。 …

    技术杂谈 2023年6月1日
    077
  • 使用Animate.css

    Animate.css是一个css动画库,可以做出一些非常好看的动画; 官网:https://animate.style Animate.css非常容易上手,但是动画是一开始就加载…

    技术杂谈 2023年7月23日
    081
  • 关于CPU的一些基本知识总结

    关于CPU和程序的执行 CPU是计算机的大脑。 程序的运行过程,实际上是程序涉及到的、未涉及到的一大堆的指令的执行过程。 当程序要执行的部分被装载到内存后,CPU要从内存中取出指令…

    技术杂谈 2023年5月31日
    0126
  • find 命令常用解释

    背景色是:orange #### find命令 find * path: 所有搜索的目录以及其所有子目录。默认为当前目录 * expression: 所有搜索的文件的特征 * cm…

    技术杂谈 2023年7月10日
    076
  • Java动态脚本Groovy,高级啊!

    前言:请各大网友尊重本人原创知识分享,谨记本人博客: 南国以南i 简介: Groovy是用于Java虚拟机的一种敏捷的动态语言,它是一种成熟的面向对象编程语言,既可以用于面向对象编…

    技术杂谈 2023年7月11日
    076
  • Oracle备份与还原(实用版)

    Oracle备份与还原 EXP&#x548C;IMP&#x662F;&#x5BA2;&#x6237;&#x7AEF;&#x5DE5;…

    技术杂谈 2023年6月21日
    0144
  • Maven中pom.xml的packaging类型

    项目的打包类型:pom、jar、war 项目中一般使用maven进行模块管理,每个模块下对应都有一个pom文件,pom文件中维护了各模块之间的依赖和继承关系。项目模块化可以将通用的…

    技术杂谈 2023年6月1日
    098
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球