发现算法之美-时间复杂度

美团校招面试算法题库大全 《2024美团校招面试算法题库大全》 关键词:美团校招、面试、算法、题库、2024 摘要: 本文旨在为准备2024年美团校招的候选人提供一套全面且深入的算法题库解析。文章将分为三大部分:第一部分深入讲解算法基础与核心概 阅读详情

在这里插入图片描述

正式工作也有3年的时间了,想要写出更加优雅的代码。

所以最近在刷leetcode补充数据结构和算法方面的知识。

学校里虽然学过,但是仅仅是有个大概的认识。只有实际工作过几年以后,才会明白数据结构和算法的重要性。

如果是通信专业出身的同学,或者是硬件出身的同学一定知道:对于一个信号,我们可以从时域和频域两个方面去分析。

那么计算机科学或者说软件开发中的算法怎么去分析呢?
有两个衡量优劣的维度:时间复杂度和空间复杂度。

  • 时间复杂度:执行当前算法所消耗的’时间’。
  • 空间复杂度:执行当前算法所占用的内存。

在这边博文中,我们来好好分析一下时间复杂度。

  • 时间复杂度真的是计算’时间’吗?
  • 时间复杂度公式:大O符号表示法
  • 常见时间复杂度类型及代码分析
    • 常数型O(1)
    • 对数型O(log n)
    • 线性型O(n)
    • 线性对数型O(n log n)
    • 平方型O(n2)、立方型O(n3)、K次方型O(n^k)
    • 平方底指数型O(2n)、立方底指数型O(3n)、K次底指数型O(k^n)
    • 阶乘型O(n!)
  • 如何理解斐波那契数列的时间复杂度O(2^N)?
  • 如何理解阶乘型时间复杂度O(n!)?
  • 参考资料

时间复杂度真的是计算’时间’吗?

把算法的执行时间当做时间复杂度?
这种方式是最为直观也是最容易想到的方式。
但是有一个问题,那就是代码在不同性能的机器上运行,以及在不同的状态下运行,会呈现出完全不同的运行时间。
比如说我有一台内存为32GB内存的mbp,还有一台8GB的台式机,假设其它的硬件条件比如cpu,主板以及机器负载状态一致。通常情况下,32GB的内存要比8GB的内存运行更快。而且这种理想状态下的只有单一变量的状态也是很难做到的。
所以不能通过计算算法的消耗时间作为时间复杂度。

那我们通常所说的’时间’复杂度中的’时间’到底是指什么呢?

聪明的前辈们想到了一种方式:大O表示法。

大O表示法内部有非常复杂的数学计算逻辑,我们偷个懒,不去证明公式,把公式用好就很厉害了。
为什么不去证明一下或者演算一遍?
我在大一曾经上过一门叫做高等代数的课,有道题目叫做:请证明1+1=2。
看到这个题目应该知道为什么不深究大O表示法背后的数学了吧。

时间复杂度公式:大O符号表示法

T(n) = O(f(n))
  • f(n)是指每行代码执行次数之和
  • f(n)可以是这些值:1,log n,n,nlog n,n2,n3,nk,2n,n!
  • f(n)与O正相关
  • O(f(n))可以是这些值:O(1),O(log n),O(n),O(nlog n),O(n2),O(n3),O(nk),O(2n),O(n!)
  • 大O表示法实际表示的是代码执行时间的增长变化趋势,不是真实的运行时间,是一种趋势预估
  • 大O表示法中的f(n)是近似值。很多时候不会完全是1,log n,n,nlog n,n2,n3,nk,2n,n!这些完整的值。例如斐波那契数列的真实时间复杂度为O(2N-1),由于N->∞,所以可以近似为O(2N)。
    更多的斐波那契数列时间复杂度的分析可以查看下文中的:如何理解斐波那契数列的时间复杂度O(2^N)?

常见时间复杂度类型及代码分析

理论扯了一大堆了,到精彩绝伦的Show me the code环节了。
先来看一张大O复杂度曲线图。

image

以下时间复杂度根据最佳->较好->一般->较差->糟糕的顺序排列。

  • 常数型O(1)
  • 对数型O(log n)
  • 线性型O(n)
  • 线性对数型O(n log n)
  • 平方型O(n2)、立方型O(n3)、K次方型O(n^k)
  • 平方底指数型O(2n)、立方底指数型O(3n)、K次底指数型O(k^n)
  • 阶乘型O(n!)
常数型O(1)
  • 常见于赋值和引用等简单操作
  • 算法消耗不随变量增长而增长,性能最佳
  • 无论代码执行多少行,即使有几千几万行,时间复杂度都为O(1)
  • 实际开发过程中,一次递归的时间复杂度也为O(1)。因为O(1^n)无论n为多少都为O(1)
let i = 0;
let j = 9;
i++;
j--;
let k = i + j;

代码分析:
i为1,j为10,k为11。
时间复杂度为O(1)。

对数型O(log n)
  • 常用代码执行次数为x,n为目标数字。符合2^x=n,推导出x=log2(n)(log n)的情况
  • 算法消耗随n的增长而增长,性能较好
let n = 100;
let i = 1;
while(i<n){
    i = i * 2
}

代码分析:
i为128。
n为100,时间复杂度为O(log2(100))。
因为Math.log2(100)≈6.64,所以最终的时间复杂度为O(6.65)。

线性型O(n)
  • 常见于一次for循环,while循环
  • 算法消耗随n的增长而增长,性能一般
  • 无论n值有多大,即使是Inifinity,时间复杂度都为O(n)
let n = 100;
let j = 0;
for(let i = 0;i<n;i++){
    j = i;
}

代码分析:
i为100,j为99。
n为100,时间复杂度为O(100)。

线性对数型O(n log n)
  • 常用于一个对时间复杂度为O(log2(n))的代码执行一个n次循环
  • 算法消耗随n的增长而增长,性能较差
let n = 100;
for(let m = 0; m<n; m++){
    let i = 1;
    while(i<n){
        i = i * 2
    }
}

代码分析:
i为128。
m为100,n为100,时间复杂度为O(m log2(n))。
因为100* Math.log2(100)≈664.39,所以最终的时间复杂度为O(664.39)。

平方型O(n2)、立方型O(n3)、K次方型O(n^k)
  • 最常见的算法时间复杂度,可用于快速开发业务逻辑
  • 常见于2次for循环,或者3次for循环,以及k次for循环
  • 算法消耗随n的增长而增长,性能糟糕
  • 实际开发过程中,不建议使用K值过大的循环,否则代码将非常难以维护
let n = 100
let v = 0;
for(let i =0;i<n;i++){
    for(let j = 0; j<n; j++){
        v = v+j+i;
    }
}

代码分析:
v为990000,i为100,j为100.
n为100,时间复杂度为O(100^2)。
也就是O(10000)。

立方型O(n3)、K次方型O(nk)和平方型O(n^2)类似,无非是多了几次循环。

// 立方型O(n^3)
for(let i =0;i<n;i++){
    for(let j = 0; j<n; j++){
        for(let m = 0; m<n; m++){

        }
    }
}
// K次方型O(n^k)
for(let i =0;i<n;i++){
    for(let j = 0; j<n; j++){
        for(let m = 0; m<n; m++){
            for(let p = 0; p<n; p++){
                ... // for循环继续嵌套下去,k值不断增大
            }
        }
    }
}
平方底指数型O(2n)、立方底指数型O(3n)、K次底指数型O(k^n)
  • 常见于2次递归的情况,3次递归以及k次递归的情况
  • 算法消耗随n的增长而增长,性能糟糕
  • 实际开发过程中,k为1时,一次递归的时间复杂度为O(1)。因为O(1^n)无论n为多少都为O(1)。

斐波那契数列(兔子数列、黄金分割数列):1、1、2、3、5、8、13、21、34···
题目:leetcode 509 斐波那契数
题解:[509.斐波那契数列 (Fibonacci Number)]https://github.com/FrankKai/leetcode-js/blob/master/509.Fibonacci_Number.js)

/**
 * @param {number} N
 * @return {number}
 */
var fib = function (N) {
  /**
   * 解法1: 递归
   * 性能:  88ms 34.2MB
   * 时间复杂度:O(2^N)
   */
  if (N <= 1) return N;
  return fib(N - 1) + fib(N - 2);
};

假设N等于100。
代码分析:
结果为 xxx。
因为浏览器直接卡死。nodejs中也运行不出来。
具体原因则是2的100次方真的太大了。算不来。
N为100,时间复杂度为O(2^100)。
因为Math.pow(2, 100)= 1.2676506002282294e+30,所以最终的时间复杂度为O(1.2676506002282294e+30)。大到爆表。

立方底指数型O(3n)、K次底指数型O(kn)与平方底指数型O(2^n)类似,只不过基数变为了3和k。

O(Math.pow(3, n))
O(Math.pow(k, n))

假设n为100,假设k为5。
Math.pow(3, n)为5.153775207320113e+47。
Math.pow(5, n)为7.888609052210118e+69。
时间复杂度也是巨高,真的是指数爆炸?。

更多的斐波那契数列时间复杂度O(2N)的分析可以查看下文中的:如何理解斐波那契数列的时间复杂度O(2N)?

阶乘型O(n!)
  • 极其不常见
  • 算法消耗随n的增长而增长,性能糟糕
function nFacRuntimeFunc(n) {
  for(let i=0; i<n; i++) {
      nFacRuntimeFunc(n-1);
  }
}

阶乘型O(n!)的时间复杂度按照(n!+(n-1)!+(n-2)!+ ··· + 1) +((n-1)!+(n-2)!+ ··· + 1)+ ··· 的方式去计算。
注意哦,这里是多个阶乘的和。不仅仅是n * (n-1) * (n-2) * (n-3)···1。
假设n从0到10,它的算法复杂度O(n!)依次为1,4,15,64,325,1956,13699,109600,986409,9864100···
为了和上文中的其它算法复杂度做比较,n为100时是多少呢?
O(2n)为10才是1024,n为100时O(2n)直接浏览器卡死了。
O(n!)才为10就接近1000万了,真要是n设置成100,计算到机器烧了也计算不出吧。

所以n为100时的O(n!)就不要想了,庞大到恐怖的一个数字。

更多的阶乘型时间复杂度O(n!)的分析可以查看下文中的:如何理解阶乘型算法复杂度O(n!)?

如何理解斐波那契数列的时间复杂度O(2^N)?

O(2^N)
  • Math.pow(base, ex),2个递归所以base是2。
  • N的话是因为N->∞,但其实真正是O(2^(N-1))。
/**
 * @param {number} N
 * @return {number}
 */
var fib = function (N) {
    /**
     * 解法1: 递归
     * 性能:  88ms 34.2MB
     */
    console.log('foo');
    if (N <= 1) return N;
    return fib(N - 1) + fib(N - 2)
};
N打印foo数O(2^N)
11O(2^0)
22^1 + 1O(2^1)
32^2 + 1O(2^2 )
42^3 + 1O(2^3 )
52^4 + 1O(2^4 )

通过上表我们分析得到:
如果包含1的话,严格来讲时间复杂度是O(2^(N-1))。
如果从N>1开始计算,时间复杂度确实是O(2^N)。
斐波那契数列非常长,N->∞,因此可以将斐波那契数列的时间复杂度直接看做是O(2^N)。

如何理解阶乘型时间复杂度O(n!)?

O(N!)

我们把上面的代码改造一下,增加一个count用来统计O(n!)。

let count = 0;
function nFacRuntimeFunc(n) {
  for(let i=0; i<n; i++) {
      count++;
      nFacRuntimeFunc(n-1);
  }
}

阶乘型O(n!)的时间复杂度按照(n!+(n-1)!+(n-2)!+ ··· + 1) +((n-1)!+(n-2)!+ ··· + 1) 的方式去计算。
注意哦,这里是多个阶乘的和。不仅仅是n * (n-1) * (n-2) * (n-3)···1。
上述示例中的count即为复杂度的值。

n多次n! + (n-1)! + ··· + 1!countO(n!)
111O(1)
2(2!+1!) +(1!)4O(4)
3(3!+(2!+1!)+1!)+((2!+1!)+1!)+(1!)15O(15)
4…64O(64)
5…325O(325)
6…1956O(1956)
7…13699O(13699)
8…109600O(109600)
9…986409O(986409)
10…9864100O(9864100)

快看看这个表格吧,n为10的时候O(n!)达到了O(9864100),接近了O(一千万)。这种算法的性能真的是糟糕到极致了。

参考资料

https://juejin.im/post/5e7c0946f265da42e879fe0c
https://zhuanlan.zhihu.com/p/50479555
https://www.bigocheatsheet.com/
https://stackoverflow.com/questions/3953244/example-of-on

期待和大家交流,共同进步,欢迎大家加入我创建的与前端开发密切相关的技术讨论小组:

努力成为优秀前端工程师!

CentOS 7 安装 Python 3.10 我这里已经软链接了,正常未链接之前是python->python2.7而不是python3.10。python2.7默认安装是在 /usr/bin目录中,切换到/usr/bin/更改yum配置,因为其要用到python2才能执行,否则会导致yum不能正常使用。2. 安装 Python 3.10 必需的 openssl >= 1.1.1。默认的centos7是没有安装pip,先添加epel扩展源。/usr/bin/python 修改为如下。添加软链接,将原来的链接备份。添加python3的软链接。 阅读详情

相关推荐

告别手动点鼠标:用Python脚本批量跑ADS版图仿真,效率提升10倍(附完整代码)

本文详细介绍了如何利用Python脚本自动化ADS版图仿真流程,显著提升射频工程师的工作效率。通过调用ADS底层接口,实现参数化修改、批量任务调度和结果自动收集,解决了手动操作中的常见问题,如仿真不更新等。附完整代码,帮助开发者快速实现10倍效率提升。

weixin_42523543的博客 331

C++中的log10函数

上机内容:求两个数相加后的位数 上机目的: 我的程序: /* * 程序的版权和版本声明部分: * Copyright (c) 2016, 信息学院 * All rights reserved. * 文件名称:求两个数想加的位数 * 作 者:邓华伟 * 完成日期:2017年3月4日 * 版 本 号:v1.0 * 对任务及求解方法的描述部分: * 输入描述:无 * 问题描述:无 * 程序输

sdau20163940的博客 1万+

车载以太网测试技术解析

在智能汽车与HiL测试领域,高效解析与模拟车载网络通信是开发验证的关键。ZD-Converter 2000以媒体/介质转换器、嗅探器、拦截器、仿真器四合一的颠覆性设计,成为工程师应对复杂车载以太网测试的瑞士军刀。它支持双路车载/标准以太网智能转换、实时通信嗅探、硬件级数据拦截及ECU通信模拟,更通过串口命令实现全参数远程配置,赋能自动化测试流程。本文将深度解析其核心功能与实战应用场景。

Triv2025的博客 1142

C++中log的底数理解

C++ 中log是以e为底的 log10 是以10为底的 现在来看看为什么底数具体为多少不重要? 读者只需要掌握(依稀记得)中学数学知识就够了。 假设有底数为2和3的两个对数函数,如上图。当X取N(数据规模)时,求所对应的时间复杂度得比值,即对数函数对应的y值,用来衡量对...

weixin_38383877的博客 8527

手把手拆解UCIe协议层:从Raw Mode到Flit Mode,数据包到底经历了什么?

本文深入解析UCIe协议层的数据包处理机制,从Raw Mode到Flit Mode的三大变体(Standard 256B、68B-Enhanced、Latency-Optimized),详细揭示数据包在不同模式下的封装与转换过程。通过工程师视角,展示协议层如何实现多模式动态切换、错误恢复及与D2D Adapter的协同设计,为芯片级互连技术提供实践指导。

weixin_28728495的博客 239

TensorFlow2深度学习实战(十四):目标检测算法 YOLOv4 解析

最易上手的【计算机视觉】小白教程!

不积跬步,无以至千里! 7635

cmath中的log函数

(1)以e为底的对数 比如:以e为底5的对数ln5 使用log(5) (2)以2为底的对数 比如:以2为底10的对数log210 使用log2(10) (3)以10为底的对数 比如:以10为底100的对数log10100 使用log10(100) (4)exp(n)值为e^n次方 (5)以n为底m的对数 我们就要使用换底公式:lognm = ln m / ln n 也就是:log(m...

化身孤岛的鲸o的博客 6678

【ArcGIS】07 ArcHydroTools对COM组件的调用返回了错误

在用Arc Hydro Tools提取流域时,在DEM Reconditioning时可能会遇到这个问题: 经过摸索,发现了问题产生的原因: 结果文件保存在了有中文的路径中。 解决的办法: 首先,把ArcGIS切换成英文版 搜索ArcGlS Administrator,以管理员方式打开 点击高级,选择语言为English 其次,新建mxd文档 删除原来的mxd文档,新建mxd文档,放置原来的图层文件。如果不这样做,最后一步的Map Name还会是“图层”这两个汉字。 最后,设置Arc Hydro T

记录专业模型开发、算法程序设计、软件学习笔记、生活日志。 4918

《算法之美》-- 读书笔记(4)-- 2019

一、书本基本信息 书名:《算法之美(将计算机科学的智慧转化为明智的生活决策)》 作者:布莱恩·克里斯汀; 汤姆· 格里菲思 出版社:中信出版社 二、读后感 1.本书将基本的计算机科学中使用的算法,应用到了生活中。针对不同的生活问题,介绍了各种各样的计算机算法。但很遗憾,全书没有公式推导和清晰的边界条件。对于想深入并清晰了解问题和算法的读者,可能需要进一步查找相关书籍。对于不是计算机专业的...

好好学习,天天向上 2127

【Bug已解决】npm install -g @anthropic-ai/claude-code 出现 EACCES: permission denied 解决方案

方案适用场景推荐指数nvm 管理 Node长期开发环境,一次配置永久生效⭐⭐⭐⭐⭐修改 npm 全局目录不想引入 nvm,快速解决⭐⭐⭐⭐修复现有目录权限之前用过 sudo 装包,需要修复⭐⭐⭐Windows 管理员/改目录Windows 平台专用⭐⭐⭐⭐官方原生安装脚本想彻底避开 npm 权限问题⭐⭐⭐⭐报的本质是npm 全局安装目录的属主权限与当前执行用户不匹配,而不是 npm 或 Claude Code 本身有问题。不要用sudo"强行"解决。

一键难忘的博客 1万+

Python爬取小红书种草商品评论数据:从入门到实战全攻略(附反爬+情感分析方案)

Python爬取小红书种草商品评论数据,核心是“抓包找到正确的接口+处理登录验证+合理的反爬策略”。新手不用一开始就追求多笔记批量爬取和情感分析,先把单笔记的评论抓取和存储跑通,再逐步扩展功能。我做的这个系统现在每天帮朋友爬取3-5款美妆产品的评论,自动做情感分析和关键词提取,她再也不用手动翻评论了,效率提升了80%。如果你们在开发中遇到具体问题,比如接口找不到、Cookie过期、IP被封,都可以在评论区留言,我会一一解答。后续也可以出一期小红书笔记种草数据爬取的教程,帮助大家更全面地分析竞品数据。

专注于Python爬虫开发,分享爬虫技巧、项目实战与反爬经验,使用Scrapy、BeautifulSoup等工具,解决数据抓取难题。 1349

算法时间复杂度_发现算法之美-时间复杂度

正式工作也有3年的时间了,想要写出更加优雅的代码。所以最近在刷leetcode补充数据结构和算法方面的知识。学校里虽然学过,但是仅仅是有个大概的认识。只有实际工作过几年以后,才会明白数据结构和算法的重要性。如果是通信专业出身的同学,或者是硬件出身的同学一定知道:对于一个信号,我们可以从时域和频域两个方面去分析。那么计算机科学或者说软件开发中的算法怎么去分析呢? 有两个衡量优劣的维度:时间复杂度和空...

weixin_39791386的博客 194

小龙虾们的好伙伴专栏导航

本文系统解析了OpenClaw技术架构,包括Channel层和网关层的设计实现,并与AgentSkills等技术方案进行对比。同时提供了OpenClaw的系列实用指南,涵盖小龙虾培养、客户资源管理、系统自动化巡检以及技术趋势助手构建等应用场景,帮助用户从零成本入门到高效落地应用,实现业务全流程自动化管理。

wend的博客 211

算法—时间复杂度

附录 log对数: 等差数列 什么是等差数列? 一般地,如果一个数列从第2项起,后一项与它的前一项的差等于同一个常数,那麽这个数列就叫做等差数列。 这个常数叫做等差数列的公差,公差通常用字母d表示。 例1:初识。 - {1,3,5,7,9} 公差为2 - {5,15,25,35,45} 公差为10 - {2,68,134,200,266} 公差为66 - {5...

点滴积累 3万+

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

算法的时间复杂度详解

时间复杂的详解,包含大O渐近表示法的讲解,冒泡排序等常见算法时间复杂度的计算分析

2301_77509762的博客 4217

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

算法的时间复杂度

算法的时间复杂度定义为: 时间复杂度或称时间复杂性,又称计算复杂度,她说是算法有效的度量之一,时间复杂度是一个算法运行时间的相对度量,一个算法的运行时间长短,它大致等于执行一种简单操作所(赋值、比较、计算、转向、返回、输入和输出)需要的时间与算法中进行简单操作次数的乘积。 根据定义,求解算法的时间复杂度的具体步骤是: ⑴找出算法中的基本语句;   一般算法中执行次数最多的那条语句就是基...

努力,奋斗! 7725

锂电池充电器电路设计 里面有电路

锂电池充电器电路设计 里面有电路 3.7V升压到5V电路设计。

初学算法——第一天:发现算法之美

算法的基本知识、特性、评判标准、时间空间复杂度等

小伟的博客 541

算法之时间复杂度

最坏情况下时间复杂度为O(n^2),但平均情况为 O(nlogn)。:O((V+E)logV),其中 V 是顶点数,E 是边数。: 取决于具体问题,一般为)O(2^n) 或O(n!: 取决于具体问题,一般为O(n^2) 或O(n*m) 等。:遍历一个长度为 �n 的列表,时间复杂度为 O(n)。: 取决于具体问题,一般为 O(nlogn) 或O(n): 最坏情况 O(n^2),平均情况 O(nlogn): 最坏情况 O(n^2),平均情况 )O(n^2): 取决于具体问题,一般为O(2^n) 或O(n!

SXXYNHHXX的博客 1245

算法的时间复杂度分析

算法的时间复杂度分析 & 递归函数时间复杂度分析算法的时间复杂度分析1.算法耗费的时间、语句频度、输入规模 在实际中,一个算法所需耗费的时间 = 算法中所有语句的执行时间之和 而, 每条语句的执行时间= 每条语句执行一次所需时间 * 每条语句的执行次数(语句频度);因为每条语句执行一次所需时间取决于机器执行指令的性能、速度等难以确定的因素,而为了独立于机器的软、硬件系统来分析算法的

Chaoer 6295
上一篇: 发现算法之美-排序
下一篇: 写了3个月TypeScript,我学到了什么?
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

打赏作者

趁你还年轻233

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值