[总结] 由数据范围反推算法复杂度以及算法内容(总结+时间复杂度+空间复杂度)


转自大佬博客总结
在这里插入图片描述


0. 前言

养成计算时间复杂度的习惯。

养成做 lc 尽量从算法角度去考虑,而不是暴力角度去考虑问题的习惯。

多算、多算、多算,不是为了做题而做题。

1. 输入数据分析

计算量在 1 0 7 10^7 107 以下最优。到了 1 0 8 10^8 108 算法常数得非常小才可以。

考虑算法常数,例如,二分、排序、哈希的算法常数就很小,循环内部操作的次数比较少。而平衡树的算法常数就很大,因为每次操作会涉及都几个、十几个的指针操作,就很慢…常数就很大,在卡数据、卡常的时候就很无奈。

<=30 的数据,大多都是指数级别的复杂度。dfs状压 dp

<100 的数据,适合 O ( n 3 ) O(n^3) O(n3) 算法,差不多到了 1 e 6 1e6 1e6 计算量,刚好。还有各式各样的 dp 问题。

<1000 的数据,适合 O ( n 2 ) O(n^2) O(n2) 或者 O ( n 2 l o g n ) O(n^2logn) O(n2logn) 算法,各式各样 dp,二分、朴素版 dijkstra、朴素版 primBellman-Ford 这几个经典的最短路算法。

<10000 的数据,这几个算法只是听说过…块状链表、分块、莫队要求都蛮高的,很难写。

<1e5 的数据,的数据方案是最常见的,各式各样的 O ( n l o g n ) O(nlogn) O(nlogn) 可供选择。

<1e6 的数据,log 1e6 大约是 20,即,20*1e6,为 2000w 的计算量,还是可以过的。但是一定是需要常数比较小的算法。针对 1e6 的算法还是采用 O ( n ) O(n) O(n) 算法居多。

<1e7 的数据,一定是 O ( n ) O(n) O(n)。线性算法,线性筛、kmp、双指针等。

n<1e9 以上的数据,都是些固定的几个了,大多都是整个题目中的一个小步骤,预处理啥的。参照上图即可。

2. 递归问题及主定理

  • 主定理参考百度百科,可以通过主定理求解递归问题的算法时间复杂度
  • 一般分析递归问题的话,例如快速排序、归并排序,我们都是将其分层,每层都是 O ( n ) O(n) O(n) 的时间,总共 l o g n logn logn 层。所以总的算法时间就是 O ( n l o g n ) O(nlogn) O(nlogn)。采用这样的方式来估计,能大致估计出一个范围。

3. 双指针

双指针算法:是可以将 O ( n 2 ) O(n^2) O(n2) 优化到 O ( n ) O(n) O(n) 的,同理也可将 O ( n 3 ) O(n^3) O(n3) 优化掉一个 n n n。平时看着是 for (i、j) 里面套 while(i, j)但是由于 i、j 都是只加不减的,所以循环体内部总共只会执行 n 次,则双指针算法就是线性的, O ( n ) O(n) O(n) 的。

4. 单调栈和单调队列

[单调栈+模板] 单调栈模板
[单调队列+模板] 单调队列模板

单调栈和单调队列和双指针算法分析类似,每个元素只会进栈、出一次,所以也是线性的。

5. kmp

[kmp+模板] kmp模板

kmp 这个时间复杂度分析也是如此,看着是有两层循环,但是当外层 for i,ji 每次增加时,匹配时 j 最多只会加 1。从定义出发,j 是严格小于 ne[j] 的,所以失配时 j=ne[j] 的操作总共最多也只会执行 n 次。所以,kmp 也是线性的 O ( n + m ) O(n+m) O(n+m)

6. 并查集

[并查集+模板] 裸并查集模板

find 看着是 O(n),但是有路径压缩存在,就是 l o g n logn logn,要是再加上按秩合并,则变为 l o g l o g n loglogn loglogn近似线性的时间复杂度。

7. 堆

[堆+模板] 模拟堆模板

取堆顶 O ( 1 ) O(1) O(1),向上调整和向下调整和高度相关,且堆为一颗完全二叉树,则高度为 O ( l o g n ) O(logn) O(logn)。至于删除、添加都是以向上调整和向下调整来完成的,故都是 O ( l o g n ) O(logn) O(logn)。关于建堆这个事情,有神奇的 O ( n ) O(n) O(n) 建堆方法 [堆+模板] 模拟堆模板,在里面也讨论了其时间复杂度证明方法。

8. 哈希表

[哈希表+模板] 模拟散列表

平均意义下,增删改查都是 O ( 1 ) O(1) O(1),赌 RP,不会发生冲突。

9. 搜索dfs与bfs

[dfs] 全排列(模板+经典)

脑补 dfsbfs 搜索树,一般是阶乘或者指数级别的。计算 dfs 的函数执行次数就行了。以全排列为例:
在这里插入图片描述

10. 图的dfs、bfs及拓扑排序

[图+拓扑排序+模板] 有向图的拓扑序列(bfs+拓扑排序+模板)

一般有判重的 st 数组存在,所以每个点、每条边只会被遍历一次,所以时间复杂度就是 O ( n + m ) O(n+m) O(n+m)。·n 是点,m 是边

同样,拓扑排序基于图的 bfs,所以也是 O ( n + m ) O(n+m) O(n+m)

11. 五大最短路及三大最小生成树

具体分析见:[图+最短路+模板] 五大最短路常用模板

  • 朴素版 dijkstra:两重循环 O ( n 2 ) O(n^2) O(n2)
  • 堆优化版 dijkstra:看着是两重 while 循环,但是实际内部保证了每个点的所有边只会做一次计算,且每次运算要有一次堆的 push 操作,所以是 O ( m l o g m ) O(mlogm) O(mlogm) 时间
  • Bellman-Ford 算法:两重循环 O ( n 2 ) O(n^2) O(n2)
  • SPFA 算法:最坏会被卡成 O ( n m ) O(nm) O(nm),但是实际运行很快,一般为 O ( m ) O(m) O(m)
    • 判断负环的话大多都是 O ( n m ) O(nm) O(nm),所以题目数据范围一般不大,2000 * 10000 左右已经是天花板了
  • Floyd 算法:三重循环, O ( n 3 ) O(n^3) O(n3)

[图+最小生成树+模板] 三大最小生成树常用模板

  • 朴素 Prim 算法:两重循环 O ( n 2 ) O(n^2) O(n2)
  • 堆优化 Prim 算法:堆优化将遍历求最值操作的 n n n 优化到 l o g n logn logn,故为 O ( n l o g n ) O(nlogn) O(nlogn)
  • Kruskal 算法:实际算法本身加上并查集后使查找每条边变成 O ( 1 ) O(1) O(1),故内部为 O ( m ) O(m) O(m),关键有个排序,使得总时间变为 O ( m l o g m ) O(mlogm) O(mlogm)

12. 染色法与匈牙利算法、二分图

[图+二分图+模板] 两大二分图常用模板

染色法: 实际上就是图的一个 dfs 或者 bfs 过程,有判重存在,故为 O ( n + m ) O(n+m) O(n+m)

匈牙利算法:最坏每次判断需要遍历所有点的所有边,则最坏效率是 O ( n m ) O(nm) O(nm) 的。但是实际运行效率非常快,远远小于 O ( n m ) O(nm) O(nm)

13. 素数筛算法

[数论+模板] 三大质数筛法(模板)

试除法: O ( n 2 ) O(n^2) O(n2) 或者 O ( n n ) O(n\sqrt n) O(nn )

朴素埃筛:千万别手残写成朴素埃筛… O ( n l o g n ) O(nlogn) O(nlogn)

埃筛: O ( n l o g l o g n ) O(nloglogn) O(nloglogn)

线性筛: O ( n ) O(n) O(n)

14. 最大公约数与快速幂

[数论+模板] 快速幂及快速幂求逆元算法模板(模板)

[数论+模板] 约数相关算法模板(模板)

gcd O ( l o g n ) O(logn) O(logn)

貌似有个库函数 __gcd(a, b)lcm 不清楚有没有

快速幂:和 k k k 位数相关,则为 O ( l o g k ) O(logk) O(logk) 的级别

15. 逆元、扩展欧几里得、各式各样组合数

[数论+模板] 裴蜀定理及扩展欧几里得算法模板(模板)

[组合] 组合数计算四大算法模板(模板+卢卡斯定理)

重点在于数学公式的推导,多为 O ( l o g n ) O(logn) O(logn) 级别,依赖于 gcd

16. 背包与dp

[背包] 背包问题算法模板(模板)

[状压dp] 蒙德里安的梦想(模板题+状压dp)

dp 看循环就行了,计算量=状态数量*状态转移数量

关键是状压 dp,以 [状压dp] 蒙德里安的梦想(模板题+状压dp) 为例,其三重循环 2 n 2^n 2n 2 n 2^n 2n m m m 就是 O ( m ( 2 n ) 2 ) O(m(2^n)^2) O(m(2n)2) 的计算量。

关于各类背包问题的时间复杂度分析,参考:[背包] 背包问题算法模板(模板) 即可。

17. 贪心

大多都是排序 O ( n l o g n ) O(nlogn) O(nlogn),要么再嵌套循环啥的,比较好分析,重点是思路。

18. 空间复杂度分析

1 Byte = 8 bit
1 KB   = 1024 Byte
1 MB   = 1024*1024 Byte
1 GB   = 1024*1024*1024 Byte

一般程序题为 64 MB 空间限制,大约开 int 数组的话可以开:2^26 / 4 = 2^24, 1600w 个 int 空间,但是要是真刚好开这么大的话,还是太憨憨了。程序的函数、寄存器啥的也是要空间的。

值得注意的一点是:申请一个小空间和申请一个大空间的花费时间几乎一样的,即准备申请一个 1000 大小的 int 数组,一次申请 1000 个和 每次申请 1 个,进行 1000 次申请,可理解为时间相差 1000 倍。

申请空间但是不用,即便超内存了,也不会报错。但是如果使用的话就会 MLE。这是操作系统的优化,申请大空间,它不会立即给你,而是在使用中发现没给你的时候会立即分配。

进行空间复杂度分析的时候,除了开的数组什么的固定空间,还要注意栈空间,典型的例子就是快速排序与归并排序的空间复杂度分析对比

  • 4
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Ypuyu

如果帮助到你,可以请作者喝水~

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

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

打赏作者

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

抵扣说明:

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

余额充值