惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
罗磊的独立博客
B
Blog RSS Feed
C
Check Point Blog
Project Zero
Project Zero
D
Docker
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
P
Palo Alto Networks Blog
L
LINUX DO - 热门话题
Scott Helme
Scott Helme
NISL@THU
NISL@THU
L
LangChain Blog
C
Cisco Blogs
Engineering at Meta
Engineering at Meta
Know Your Adversary
Know Your Adversary
雷峰网
雷峰网
S
Schneier on Security
MyScale Blog
MyScale Blog
博客园_首页
博客园 - 三生石上(FineUI控件)
C
CERT Recently Published Vulnerability Notes
美团技术团队
V
Visual Studio Blog
T
The Exploit Database - CXSecurity.com
Recent Announcements
Recent Announcements
G
GRAHAM CLULEY
T
Tor Project blog
V
Vulnerabilities – Threatpost
U
Unit 42
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Stack Overflow Blog
Stack Overflow Blog
P
Privacy International News Feed
Security Latest
Security Latest
W
WeLiveSecurity
aimingoo的专栏
aimingoo的专栏
Hugging Face - Blog
Hugging Face - Blog
Google Online Security Blog
Google Online Security Blog
V2EX - 技术
V2EX - 技术
The Last Watchdog
The Last Watchdog
博客园 - Franky
T
Tenable Blog
云风的 BLOG
云风的 BLOG
D
DataBreaches.Net
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
Latest news
Latest news
N
News and Events Feed by Topic
Cloudbric
Cloudbric
Schneier on Security
Schneier on Security
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Blog — PlanetScale
Blog — PlanetScale

青空之蓝

[青空之蓝-2023] - 色彩 | 青空之蓝 [青空之蓝-2022] - 平静 | 青空之蓝 [青空之蓝-2021] - 远望 | 青空之蓝 浅谈垃圾回收 | 青空之蓝 浅谈泛型擦除 | 青空之蓝 浅谈单点登录 | 青空之蓝 使用 Kotlin 编写 Spring 测试 | 青空之蓝 设计模式系列文章 | 青空之蓝 从零实现一个 Java 微框架 - IoC | 青空之蓝 从零实现一个 Java 微框架 - 前言 | 青空之蓝 浅谈 JVM:类加载 | 青空之蓝 浅谈 IO | 青空之蓝 浅谈并发:synchronized & ReentrantLock | 青空之蓝 浅谈并发:CAS & AQS | 青空之蓝 浅谈并发:ThreadLocal | 青空之蓝 浅谈并发:三大特性 | 青空之蓝 浅谈组合注解 & 注解别名 | 青空之蓝 [青空之蓝-2020]-迷茫 | 青空之蓝 Java 系列文章 | 青空之蓝 HTTP 系列文章 | 青空之蓝 浅谈 EatWhatYouKill | 青空之蓝 浅谈可扩展线程池 | 青空之蓝 聊聊写框架 | 青空之蓝 聊聊现状-[2020-09] | 青空之蓝 浅谈并发:锁 | 青空之蓝 浅谈并发:基础 | 青空之蓝 浅谈缓存 | 青空之蓝 无须定义类,Spring 快速注入 Json 参数 | 青空之蓝 浅谈 Proxy 和 Aop | 青空之蓝 从零实现一个 PHP 微框架 - 初始化请求 | 青空之蓝 为 Vue3 添加一个简单的 Store | 青空之蓝 从零实现一个 PHP 微框架 - 服务提供者 | 青空之蓝 WSL2 踩坑记录 | 青空之蓝 浅谈浏览器Event Loop [更新] | 青空之蓝 从零实现一个 PHP 微框架 - Bootstrap 启动加载 | 青空之蓝 从零实现一个 PHP 微框架 - IoC 容器 | 青空之蓝 从零实现一个 PHP 微框架 - PSR & Composer | 青空之蓝 从零实现一个 PHP 微框架 - 前言 | 青空之蓝 MVVM 简单实现 | 青空之蓝 浅谈 DI 和 IoC | 青空之蓝 中间件实现 [PHP] | 青空之蓝 告别 Windows 终端的难看难用,打造好用的 PowerShell | 青空之蓝 VSCode Java输出中文乱码问题解决[更新] | 青空之蓝 浅谈浏览器渲染 | 青空之蓝 Vue-Cli@2 项目迁移日志 | 青空之蓝 Laragon & Scoop 集成踩坑记录 | 青空之蓝 「一行代码」优雅管理 Windows 软件 | 青空之蓝 [青空之蓝-2019]-年度总结 | 青空之蓝 为Vue添加简单的Store | 青空之蓝 为React添加简单的Store | 青空之蓝 为Vuex添加同步Action | 青空之蓝 浅谈B+树 | 青空之蓝 浅谈跳表 | 青空之蓝 浅谈数据库索引 | 青空之蓝 MySQL事务隔离 | 青空之蓝 一年来的经验总结 | 青空之蓝 Acrylic - VSCode Extension | 青空之蓝 ace编辑器设置惯性滚动 | 青空之蓝 Java二叉树实现 | 青空之蓝 Java图实现 | 青空之蓝 为apt方式安装的nginx重新编译增加WebDAV | 青空之蓝 XK-Editor - 一个支持富文本和Markdown的编辑器 | 青空之蓝 JS生成列表树 | 青空之蓝 Laravel生成目录树 | 青空之蓝 XK-Note - 集各种神奇功能的云笔记 | 青空之蓝 PHP GD生成验证码 | 青空之蓝 PHP GD图片处理[转换格式-水印-缩略图] | 青空之蓝 Origami - 简洁轻快的WordPress主题 | 青空之蓝 为WordPress启用WorkBox | 青空之蓝 [青空之蓝-2018]-年度总结 | 青空之蓝 VSCode Java手动导入jar和源码包 | 青空之蓝 Windows IP变化自动发送邮件 | 青空之蓝 C链表实现重制版 | 青空之蓝 C 结构体的定义和使用 | 青空之蓝 图的搜索(遍历) - BFS & DFS | 青空之蓝 Java链表实现 | 青空之蓝 C 快速排序 | 青空之蓝 C 插入排序 | 青空之蓝 C 归并排序 | 青空之蓝 C语言链表实现 | 青空之蓝 VSCode配置Java调试环境[Windows] | 青空之蓝 C 选择排序 | 青空之蓝 C 冒泡排序 | 青空之蓝 VSCode配置PHP调试环境[Windows] | 青空之蓝 VSCode配置C/C++ GDB调试环境[Windows] | 青空之蓝 WordPress友情链接模板 | 青空之蓝 Intel Optane 傲腾内存体验 | 青空之蓝 Mysql双机热备实战 | 青空之蓝 博客一年记录 | 青空之蓝 为WordPress启用Service Worker | 青空之蓝 Bing每日一图API | 青空之蓝 iframe延迟加载 | 青空之蓝 写在2018年高考前 | 青空之蓝 The Fox主题汉化分享 | 青空之蓝 [青空之蓝-2017]-崭新 | 青空之蓝 本博客评论规则 | 青空之蓝 世界,您好! | 青空之蓝
算法复杂度分析(1) | 青空之蓝
Otstar Lin · 2019-08-28 · via 青空之蓝

为什么需要算法复杂度分析,实际运行一遍然后收集运行的信息和统计不就可以了?这总事后分析的方法确实没有错,但是当环境发生变化,数据量发生变化,或者数据模型改变了,程序的执行效率就有可能发生巨大的改变,所以我们需要一种粗略的不需要具体测试数据的估算程序执行效率的方法,而这种方法就是复杂度分析。

大 O 复杂度表示法

大 O 表示法是一种特殊的表示法,指出了算法的速度有多快或者多慢。谁在乎呢?实际上,你经常要使用别人编写的算法,在这种情况下,知道这些算法的速度大有裨益。

这里我们拿一段代码来进行解释:

int fun(int n) {
    int num = 0;
    for (int i = 1; i <= n; i++) {
        sum = sum + i;
    }
    return sum;
}
int fun(int n) {
    int num = 0;
    for (int i = 1; i <= n; i++) {
        sum = sum + i;
    }
    return sum;
}

这段代码是用来求数列和的,我们把每一句代码执行的时间记为 1u,然后默认每句基础代码执行的时间都是一样的,从上面的代码我们可以看出,int num = 0;int i = 1;各执行了 1 次,sum = sum + i;i++各执行了 n 次,return部分我们不看,那么这个函数所执行的总时间就应该是(2n+2)*1u,我们把总时间记为 T(n)那么,可以看出执行的总时间和 n 成正比关系,即和执行次数成正比关系,而这种规律我们可以总结成一个公式即:

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

而上一个公式就可以表示成T(n) = O(2n+2),当 n 到大一定程度的时候我们就可以把+2 和 n 的系数 2 给忽略掉,这时候的T(n) = O(n),这就是我们常说的 O(n)时间复杂度或 O(n)空间复杂度的由来。

时间复杂度分析

那么当代码越来越长,越来越庞大是时候难道还这样一步步分析吗?当然不是,当我们对算法或者程序进行分析的时候我们只需要关注以下几点即可:

1. 只关注循环次数最多的代码

还是拿上面一段代码来分析。

int fun(int n) {
    int num = 0;
    for (int i = 1; i <= n; i++) {
        sum = sum + i;
    }
    return sum;
}
int fun(int n) {
    int num = 0;
    for (int i = 1; i <= n; i++) {
        sum = sum + i;
    }
    return sum;
}

从代码中我们可以看出,只有一个循环 n 次的 for 循环,而其他部分的代码只执行了一次,这时我们就可以直接忽略掉那些只执行一次的代码,只关注执行最多的代码,即 for 循环中的代码,所以这个函数的时间复杂度就是 O(n)。

2. 加法法则:只关注执行次数最多的代码的时间复杂度

int fun(int n) {
    for (int i = 0; i < 10; i++) {
        System.out.println("a");
    }
    for (int i = 0; i < n; i++) {
        System.out.println("a");
    }
    for (int i = 0; i < n*10; i++) {
        System.out.println("a");
    }
    for (int i = 0; i < n*n; i++) {
        System.out.println("a");
    }
}
int fun(int n) {
    for (int i = 0; i < 10; i++) {
        System.out.println("a");
    }
    for (int i = 0; i < n; i++) {
        System.out.println("a");
    }
    for (int i = 0; i < n*10; i++) {
        System.out.println("a");
    }
    for (int i = 0; i < n*n; i++) {
        System.out.println("a");
    }
}

从上面的代码我们可以看到 4 个 for 循环这时候应该如何判断呢?首先我们先找出它们各自循环了几次,第一个执行了 10 次,第二个执行了 n 次,第三个执行了 10n 次,第四个执行了 n2 次,这时候我们只需要关注执行最多的那个循环的复杂度即可,即第四次(我们均默认 n 很大),所以这段函数的时间复杂度为 O(n2)。

3. 乘法法则:嵌套代码的时间复杂度等于嵌套内外的时间复杂度的乘积

int fun(int n) {
    for (int i = 0; i < n; i++) {
        cal(n);
    }
}

int cal(int n) {
    for (int i = 0; i < n; i++) {
        System.out.println("a");
    }
}
int fun(int n) {
    for (int i = 0; i < n; i++) {
        cal(n);
    }
}

int cal(int n) {
    for (int i = 0; i < n; i++) {
        System.out.println("a");
    }
}

从上方代码看出,fun 函数中的 for 循环每次循环都需要调用一次 cal 函数,而我们可以很轻松的判断出 cal 函数的时间复杂度是 O(n),而 fun 一共需要调用 n 次 cal 函数,即 O(n) * O(n),即 O(n*n),简化下就是 O(n2)。

到这里我们就基本讲完了时间复杂度的分析方法了,下面我们就来看以下几种常见 的算法时间复杂度。

番外:常见的时间复杂度

1. O(1)

这个时间复杂度并不是表示只有一句基础代码,而是代表常数的时间复杂度,即使你有 100 句基础代码也不能表示为 O(100),而应该表示为 O(1)。

2. O(logn), O(nlogn)

对数阶时间复杂度非常常见,同时也是最难分析的一种时间复杂度。 我这里就拿算法图解这本书中的一张图来说明:

来自《算法图解》

我们要得到一张 16 格的纸,如果采用对半折的方式就只需要 4 次就能完成,即 4 = log216 若把 4 换成 T(n),16 换成 n,我们就可以得到 T(n) = O(log2n),然后我们忽略掉常数的底数,就变成了 O(logn),并不是只有 2 才能忽略掉底数,所有的常数底数都可以被忽略。

3. O(m+n)、O(m*n)

或许你有看过这种复杂度,或许没有看过,这种其实非常简单,我们通过下面的例子来进行分析:

int fun(int m, int n) {
    for (int i = 0; i < m; i++) {
        System.out.println("a");
    }
    for (int i = 0; i < n; i++) {
        System.out.println("a");
    }
}
int fun(int m, int n) {
    for (int i = 0; i < m; i++) {
        System.out.println("a");
    }
    for (int i = 0; i < n; i++) {
        System.out.println("a");
    }
}

当我们看到这个函数的时候是不是就应该通过加法法则排除掉一个呢?然后你会发现若不知道 m 和 n 的大小关系根本无法对其进行判断,也就无法排除掉其中的一项,这时候就应该保留两个未知数,即 O(m + n),另外的 O(m * n)同理。

空间复杂度分析

空间复杂度的分析十分的简单,我们直接通过一个例子来进行分析吧:

int fun(int n) {
    int[] arr = new int[n];
    return arr;
}
int fun(int n) {
    int[] arr = new int[n];
    return arr;
}

这段例子非常简单,就是创建了一个大小为 n 的数组,所以这段代码的时间复杂度就是 O(n)。

结语

复杂度的分析其实不难,只要多练习,多分析就能熟练的掌握它,还有最好时间复杂度,最坏时间复杂度,以及平均时间复杂度等一些分析我留着下一篇在说吧,零零散散的写了一篇文章,还是挺累的,完全手打了 2300+个字,其实是懒 (逃,( ̄ y▽, ̄)╭ 。

算法复杂度分析(1)

https://blog.ixk.me/post/algorithm-complexity-analysis-1
  • 许可协议

    BY-NC-SA

  • 本文作者

    Otstar Lin

  • 发布于

    2019/08/28

转载或引用本文时请遵守许可协议,注明出处、不得用于商业用途!

MySQL事务隔离一年来的经验总结