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

推荐订阅源

L
LangChain Blog
阮一峰的网络日志
阮一峰的网络日志
Y
Y Combinator Blog
博客园 - 聂微东
GbyAI
GbyAI
H
Hackread – Cybersecurity News, Data Breaches, AI and More
月光博客
月光博客
T
The Blog of Author Tim Ferriss
爱范儿
爱范儿
宝玉的分享
宝玉的分享
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
雷峰网
雷峰网
MyScale Blog
MyScale Blog
Cloudbric
Cloudbric
T
Threatpost
Scott Helme
Scott Helme
N
News | PayPal Newsroom
Google DeepMind News
Google DeepMind News
Martin Fowler
Martin Fowler
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Engineering at Meta
Engineering at Meta
美团技术团队
Apple Machine Learning Research
Apple Machine Learning Research
V2EX - 技术
V2EX - 技术
罗磊的独立博客
Attack and Defense Labs
Attack and Defense Labs
IT之家
IT之家
S
Secure Thoughts
C
Cyber Attacks, Cyber Crime and Cyber Security
D
Docker
V
V2EX
T
Troy Hunt's Blog
Forbes - Security
Forbes - Security
aimingoo的专栏
aimingoo的专栏
J
Java Code Geeks
Last Week in AI
Last Week in AI
C
CERT Recently Published Vulnerability Notes
T
Tor Project blog
P
Proofpoint News Feed
Recorded Future
Recorded Future
B
Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Simon Willison's Weblog
Simon Willison's Weblog
A
About on SuperTechFans
The GitHub Blog
The GitHub Blog
MongoDB | Blog
MongoDB | Blog
O
OpenAI News
V
Vulnerabilities – Threatpost
P
Privacy International News Feed
云风的 BLOG
云风的 BLOG

清竹志-(原清竹茶馆)

读完 DeepSeek-V4 技术报告:这次最值得看的,不是“更大”,而是“更省” Codex Skills 不是 Prompt 的升级版,而是写给 AI 的岗位 SOP 【译】React 服务器组件中的关键安全漏洞 Vercel收购NuxtLabs,我和尤雨溪一样心情复杂 免费平替Claude-Code:Google Gemini CLI深度解析与实践指南 Newsletter逆天福利:三种方式获取全年免费 AI/SaaS 工具包(含团体购买兑换防坑指南) Google发布Agent2Agent(A2A)协议:开启协作式 AI 代理的新时代 颠覆传统搜索!全球AI搜索工具正在攻城略地 激战“后DeepSeek时代”:全球大模型开启“地狱级”内卷模式 改名公告:清竹茶馆的新篇章-清竹志 【2023年终总结】与逝去的年华道个别 【前端工程化】Nextjs项目工程化最佳实践总结(献给2023-1024的礼物) 【2022年终总结】此去经年,烟消雾散,晨曦微露 【1024献礼】生成可掌握钱包和揭秘NFT和token被盗案例以及如何预防被盗 【2021年终总结】刚好遇见你,留下百年的期许 【Swift】Swift学习计划与资料 【前端工程化】Vite关于Vue3/React项目工程化总结(献给2021-1024的礼物) 【三亚攻略】2021年三亚团建攻略 【前端工具】前端工具函数合集 【2020年终总结】如果这一年你很健康,那便是最好的一年!
【图解算法】前端思维学习图解算法数据结构笔记(一)之算法复杂度
vadxq · 2021-09-22 · via 清竹志-(原清竹茶馆)

【图解算法】前端思维学习图解算法数据结构笔记(一)之算法复杂度

发表更新算法 / 图解算法 / 算法复杂度18 分钟读完 (大约2732个字)0次访问

【图解算法】前端思维学习图解算法数据结构笔记(一)之算法复杂度

书名是《图解算法数据结构》,在力扣看到的,准备开始系统学习一下算法了。会用前端的语言和思维来学习学习~希望能获得一些见解。第一篇是关于算法复杂度的学习笔记记录。

算法复杂度

复杂度概念

书中介绍,算法复杂度旨在计算在输入数据量 N 的情况下,算法的时间使用空间使用情况;体现算法运行使用的时间和空间随「数据大小N」而增大的速度

  • 时间:假设各操作的运行时间为固定常数,统计算法运行的计算操作的数量,以代表算法运行所需时间
  • 空间:统计在最差情况下,算法运行所需使用的”最大空间”
  • 数据大小N:算法处理的输入数据量;根据不同算法,具有不同定义
    • 排序算法:N 代表需要排序的元素数量
    • 搜索算法:N 代表搜索范围的元素总数

理解:算法复杂度是在一定的输入数据量下,计算所需要的时间操作数量大小(计算操作数量)和空间的大小来体现复杂度量。

时间复杂度

概念:时间复杂度指输入数据大小为 N 时,算法运行所需花费的时间

注意的是,这里的时间是计算操作数量,和运行绝对时间呈正相关关系,并不相等。

时间复杂度具有最差平均最佳三种情况,分别使用O, Θ, Ω三种符号表示

1
2
3
4
5
6
7
8
9
10
11
12
13

const find_num = (nums) => {
for (const num in nums) {
if (num === 7) return false
}
return false
}






常见的算法时间复杂度大小排序:O(1) < O(logN) < O(N) < O(NlogN) < O(N^2) < O(2^N) < O(N!)

  • 常数O(1):不随输入数据大小 N 的变化而变化
  • 线性O(N):循环运行次数与 N 大小呈线性关系
  • 平方O(N^2):两层循环相互独立,都与 N 呈线性关系,因此总体与 N 呈平方关系
  • 指数O(2^N):指数阶常出现于递归
  • 阶乘O(N!):阶乘阶对应数学上常见的 “全排列” 。即给定 NN 个互不重复的元素,求其所有可能的排列方案。阶乘常使用递归实现,算法原理:第一层分裂出 N 个,第二层分裂出 N−1 个,…… ,直至到第 N 层时终止并回溯
  • 对数O(logN):对数阶与指数阶相反,指数阶为 “每轮分裂出两倍的情况” ,而对数阶是 “每轮排除一半的情况”
  • 线性对数O(NlogN):两层循环相互独立,第一层和第二层时间复杂度分别为 O(logN) 和 O(N)

个人理解

  • 常数O(1):就是传入变量不影响计算,相当于一个未使用的变量。eslint下应该是遇不到的
  • 线性O(N):与输入N线性关系,其中只有一次循环是和输入的N有关
  • 平方O(N^2):两层互相独立的线性关系嵌套,两层嵌套循环,比如冒泡排序
  • 指数O(2^N): 树形图,每个节点都有两个操作,节节往下,第一层祖父节点,2^0 ,第二层就是2^1 ,以此类推,最后就是 2^n-1 次方,加起来就是 2^n
  • 阶乘O(N!):全排列,和上面反过来,第一次=层全部循环一遍,然后第二层操作剔除一个特征操作,以此类推
  • 对数O(logN):和指数反过来,就是二分法,分治这样的算法基础,一分为二或者一分为多。
  • 线性对数O(NlogN):两层循环相互独立,第一层和第二层时间复杂度分别为O(logN)和O(N)

空间复杂度

概念:空间复杂度指在输入数据大小为 N 时,算法运行所使用的暂存空间+输出空间的总体大小

  • 输入空间: 存储输入数据所需的空间大小;输入数据
  • 暂存空间: 算法运行过程中,存储所有中间变量和对象等数据所需的空间大小;包括数据空间``栈帧空间``指令空间
    • 数据空间:各项变量使用的空间,包括:声明的常量、变量、动态数组、动态对象等使用的内存空间。
    • 栈帧空间:程序调用函数是基于栈实现的,函数在调用期间,占用常量大小的栈帧空间,直至返回后释放。
    • 指令空间:编译后,程序指令所使用的内存空间。
  • 输出空间: 算法运行返回时,存储输出数据所需的空间大小;输出数据

最差情况:空间复杂度统计算法在 “最差情况” 下使用的空间大小,以体现算法运行所需预留的空间量,使用符号O表示,包含两种情况

  • 最差输入数据:当 N≤10 时,数组 nums 的长度恒定为 10 ,空间复杂度为 O(10) = O(1);当 N > 10时,数组 nums 长度为 N ,空间复杂度为 O(N) ;因此,空间复杂度应为最差输入数据情况下的 O(N)
  • 最差运行点:在执行nums = Array(10).fill(0)时,算法仅使用 O(1) 大小的空间;而当执行 nums = Array(n).fill(0)时,算法使用 O(N) 的空间;因此,空间复杂度应为最差运行点的 O(N)

常见的算法空间复杂度有:O(1) < O(logN) < O(N) < O(N^2) < O(2^N)

  • 常数O(1):普通常量、变量、对象、元素数量与输入数据大小 N 无关的集合,皆使用常数大小的空间
  • 线性O(logN):元素数量与 N 呈线性关系的任意类型集合(常见于一维数组、链表、哈希表等)
  • 平方O(N):元素数量与 N 呈平方关系的任意类型集合(常见于矩阵),皆使用平方大小的空间
  • 指数O(N^2):指数阶常见于二叉树、多叉树
  • 对数O(2^N):对数阶常出现于分治算法的栈帧空间累计、数据类型转换等。如快速排序/数字转化字符串

时空权衡

对于算法的性能,需要从时间和空间的使用情况来综合评价。优良的算法应具备两个特性,即时间和空间复杂度皆较低。而实际上,对于某个算法问题,同时优化时间复杂度和空间复杂度是非常困难的。降低时间复杂度,往往是以提升空间复杂度为代价的,反之亦然。

由于当代计算机的内存充足,通常情况下,算法设计中一般会采取空间换时间的做法,即牺牲部分计算机存储空间,来提升算法的运行速度。

一些DEMO

时间复杂度相关的demo

1
2
3
4
5
6
7
8
9
10
11
const algorithm = (n) => {
let count = 0;
const a = 100;
for (const i in n) {
for (const j in a) {
count += 1;
}
}
return count;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

const bubbleSort = (nums) => {
const n = nums.length;
for (const i in n - 1) {
for (const j in n - i - i) {
if (nums[j] > nums[j + 1]) {
cache = nums[j]
nums[j] = nums[j + 1]
nums[j + 1] = cache
}
}
}
return nums;
}

1
2
3
4
5
6
7
8

const algorithm = (n) => {
if (n <= 0) return 1
const a = algorithm(n - 1)
const b = algorithm(n - 1)
return a + b;
}

1
2
3
4
5
6
7
8
9
10

const algorithm = (n) => {
if (n <= 0) return 1
let count = 0
for (const i in n) {
count = algorithm(n - 1)
}
return count;
}

1
2
3
4
5
6
7
8
9
10
11

const algorithm = (n) => {
let count = 0
let i = n
while (i > 1) {
i = i / 2
count += 1
}
return count
}

1
2
3
4
5
6
7
8
9
10

const algorithm = (n) => {
let count = 0
let i = n
while (i > 1) {
i = i / 2
for (const j in n) count += 1
}
}

空间复杂度的相关demo

1
2
3
4
5
6
const child = () => 0

const parent = (n) => {
for (const i in n) child()
}

1
2
3
4
5
6

const parent = (n) => {
if (n <= 1) return 1
return parent(n - 1) + 1
}

1
2
3
4
5
6

const algorithm = (n) => {
const num = 5
let nums = Array(10).fill(0)
if (n > 10) nums = Array(n).fill(0)
}

时空权衡的demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52


var twoSum = function(nums, target) {
const len = nums.length;
for (let i = 0; i < len - 1; i++) {
for (let j = i + 1; j < len; j++) {
if (nums[i] + nums[j] === target) {
return [i, j]
}
}
}
};




const twoSum = function(nums, target) {
const len = nums.length;


const map = new Map();
for (let i = 0; i < len; i++) {
map.set(nums[i], i);
}

for (let i = 0; i < len; i++) {
const needNum = target - nums[i];

if (map.has(needNum) && i !== map.get(needNum)) {
return [i, map.get(needNum)]
}
}
}



const twoSum = function(nums, target) {
const len = nums.length;
const map = new Map();

for (let i = 0; i < len; i++) {
const needNum = target - nums[i];

if (map.has(needNum) && i !== map.get(needNum)) {
return [i, map.get(needNum)];
}


map.set(nums[i], i);
}
}

评论