算法中的时间复杂度和空间复杂度

一、背景

        随着人工智能的纵深发展,我们会发现现在做算法很多时候都是通过掉包来解决问题了。Torch或者Tensorflow之类的深度学习库大大减少了算法工程师的工作量,而且在张量运算、反向传播等环节,这些深度学习库的模块设计也尽最大可能地降低了计算的时间和空间复杂度,从而不需要我们额外进行过多的干预。

        如果不是科班读计算机相关专业的,相信不少朋友第一次听说时间复杂度和空间复杂度的概念是在找工作刷leetcode题的时候。但实际上在编程的过程中,时间复杂度和空间复杂度一直都是我们需要关注的内容。即便深度学习建模的过程不需要过多考虑,但在数据预处理、数据后处理等阶段,我们仍离不开对低时间复杂度和低空间复杂度代码的追求。同样一份数据,高效的数据处理程序有时候要比低效的程序效率高出数倍甚至上百倍,而这也是体现一个算法工程师专业能力的重要环节。

二、时间复杂度

        时间复杂度是指算法执行所需的时间随输入规模的增长而增长的速率。它通常用大O符号(Big O notation)表示,描述了最坏情况下的时间增长趋势。时间复杂度帮助我们评估算法的效率,特别是在处理大规模数据时,一个时间复杂度低的算法通常比时间复杂度高的算法更快。一般来说,基本的时间复杂度类型包括以下几种(复杂度:O(1) < O(log n) < O(n) < O(n^2) < O(2^n)):

1、常数时间复杂度 O(1)

        算法的执行时间不随输入规模的变化而变化。例如访问数组中的某个元素,或者定义某个变量。一般来说只要不涉及循环的代码时间复杂度都是常数级的。

2、对数时间复杂度 O(log n)

        算法的执行时间与输入规模的对数成正比。例如,当我们需要遍历一个有序数组并从中找到我们想要的数字对应的索引时,我们可以遍历这个数组逐个比对当前索引对应的元素是否是我们要找的,这时候的时间复杂度为O(n),但如果我们使用二分查找则可以将时间复杂度缩短至O(log n)。一般来说,涉及二分法的程序都是对数时间复杂度,因为每次遍历都只对有效的那一半数据进行操作。

nums = [1, 3, 5, 6, 7, 8, 9, 10]
target = 6

# 遍历数组查找是线性的时间复杂度O(n)
for i in range(len(nums)):
    if nums[i]==target:
        print(i)

# 使用二分查找
def binary_search(nums, target):
    left, right = 0, len(nums)
    while left<=right:
        mid = (left+right)//2
        if nums[mid]>target:
            right = mid-1
        elif nums[mid]<target:
            left = mid+1
        else:
            return mid
    return left

index = binary_search(nums, target)
print(index)

3、线性时间复杂度 O(n)

        算法的执行时间与输入规模成正比。例如遍历一个长度为n的数组。下面的代码遍历了数组nums的每一个数字,那么它的时间复杂度就是线性的,最差时间复杂度为数组长度。一般来说,使用了一层for或者while循环的程序都是线性时间复杂度的        

nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
for num in nums:
    print(num)

4、平方时间复杂度 O(n^2)

        算法的执行时间与输入规模的平方成正比。基本上两层for或者while就是平方时间复杂度了,三层循环就是O(n^3),以此类推。

nums = [1, 2, 3, 4, 5, 10, 6, 7, 8, 9]

# 这里使用平方时间复杂度的两层循环找出有序列表中唯一乱序的位置
for i in range(len(nums)):
    for j in range(i+1, len(nums)):
        if nums[j]<nums[i]:
            print(i)
            break

5、指数时间复杂度 O(2^n)

        算法的执行时间随着输入规模的指数增长。例如,下面这个计算斐波那契数列的程序就是指数级别的时间复杂度,因为它会重复计算相同的子问题。

def fibonacci(n):
    if n <= 0:
        return 0
    elif n == 1:
        return 1
    else:
        return fibonacci(n-1) + fibonacci(n-2)

# 测试
n = 10
print(f"Fibonacci({n}) = {fibonacci(n)}")

        对上面的这个代码,我们使用动态规划进行优化,就可以避免子问题的重复计算,从而将时间复杂度优化至O(n)。

def fibonacci_dp(n):
    if n <= 0:
        return 0
    elif n == 1:
        return 1

    # 存储每一步的结果,后面的计算就不需要再重复
    fib = [0] * (n + 1)
    fib[1] = 1
    
    for i in range(2, n + 1):
        fib[i] = fib[i-1] + fib[i-2]
    
    return fib[n]

# 测试
n = 10
print(f"Fibonacci({n}) = {fibonacci_dp(n)}")

三、空间复杂度

        空间复杂度是指算法在执行过程中所需的内存空间随输入规模的增长而增长的速率。它也用大O符号表示。空间复杂度帮助我们评估算法的内存使用情况,特别是在内存资源有限的环境中。在开发过程中,了解空间复杂度可以帮助我们选择最合适的数据结构,以满足内存使用要求。常见复杂度如下(复杂度:O(1) < O(log n) < O(n) < O(n^2)):

1、常数空间复杂度 O(1)

        算法所需的空间不随输入规模的变化而变化。例如定义一个含有固定值的变量。

2、对数空间复杂度 O(log n)

        算法所需的空间与输入规模的对数成正比。例如二分法等递归深度为log n的递归算法,其空间复杂度一般也是对数级别的

3、线性空间复杂度 O(n)

        算法所需的空间与输入规模成正比。例如,使用一个长度为n的数组存储数据,这时候的空间复杂度会随着数组规模的扩大而线性增长。

nums = []
for i in range(100):
    nums.append(i)

4、平方空间复杂度 O(n^2)

        算法所需的空间与输入规模的平方成正比。例如,使用一个m x n的二维数组。

# 创建一个大小为10*10的矩阵
m = 10
n = 10
matrix = [[0]*n for _ in range(m)]

四、总结

        除了上面介绍的常见复杂度类型,还有各种组合的复杂度类型,例如O(m+n)、O(n*log n)等。这些是由于程序中使用了一些嵌套,从而导致复杂度的组合多种多样。例如在一个线性的for循环层中,对每一次的循环都使用一次二分查找,其时间复杂度就是O(n*log n)了。但是不论什么样的组合,都离不开上面的基础类别。

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/958493.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2025 OWASP十大智能合约漏洞

随着去中心化金融&#xff08;DeFi&#xff09;和区块链技术的不断发展&#xff0c;智能合约安全的重要性愈发凸显。在此背景下&#xff0c;开放网络应用安全项目&#xff08;OWASP&#xff09;发布了备受期待的《2025年智能合约十大漏洞》报告。 这份最新报告反映了不断演变的…

双指针+前缀和习题(一步步讲解)

前言&#xff1a;如果解决下面这几道题有些问题&#xff0c;或者即使看了我画的过程图也不理解的可以去看看我的上一篇文章&#xff0c;有可能会对你有帮助。 一、《数值元素的目标和》---来自AcWing 数组元素的目标和 给定两个升序排序的有序数组 A和 B&#xff0c;以及一个…

单路由及双路由端口映射指南

远程登录总会遇到登陆不上的情况&#xff0c;可能是访问的大门没有打开哦&#xff0c;下面我们来看看具体是怎么回事&#xff1f; 当软件远程访问时&#xff0c;主机需要两个条件&#xff0c;一是有一个唯一的公网IP地址&#xff08;运营商提供&#xff09;&#xff0c;二是开…

Addressable学习

AssetsBundle是Unity的资源管理机制,将资源打包到AssetsBundle资源包并提供接口能从ab包里面加载资源出来。有了这个机制以后&#xff0c;我们要做资源管理&#xff0c;还需要做: a: 根据项目需求,编写编辑器扩展,提供指定资源打入对应bundle包工具策略; b: 根据项目的需求,资源…

大华相机DH-IPC-HFW3237M支持的ONVIF协议

使用libONVIF C库。 先发现相机。 配置 lib目录 包含 编译提示缺的文件&#xff0c;到libonvif里面拷贝过来。 改UDP端口 代码 使用msvc 2022的向导生成空项目&#xff0c;从项目的main示例拷贝过来。 CameraOnvif.h #pragma once#include <QObject> #include &l…

leetcode刷题记录(八十六)——84. 柱状图中最大的矩形

&#xff08;一&#xff09;问题描述 84. 柱状图中最大的矩形 - 力扣&#xff08;LeetCode&#xff09;84. 柱状图中最大的矩形 - 给定 n 个非负整数&#xff0c;用来表示柱状图中各个柱子的高度。每个柱子彼此相邻&#xff0c;且宽度为 1 。求在该柱状图中&#xff0c;能够勾…

centos9编译安装opensips 二【进阶篇-定制目录+模块】推荐

环境&#xff1a;centos9 last opensips -V version: opensips 3.6.0-dev (x86_64/linux) flags: STATS: On, DISABLE_NAGLE, USE_MCAST, SHM_MMAP, PKG_MALLOC, Q_MALLOC, F_MALLOC, HP_MALLOC, DBG_MALLOC, CC_O0, FAST_LOCK-ADAPTIVE_WAIT ADAPTIVE_WAIT_LOOPS1024, MAX_RE…

SpringBoot 实现动态管理定时任务 Job的动态操作(添加、修改、启停、执行、删除)以及界面展示和具体Job的创建与执行示例

SpringBoot 实现动态管理定时任务 Job的动态操作&#xff08;添加、修改、启停、执行、删除&#xff09;以及界面展示和具体Job的创建与执行示例 关键接口类&#xff1a; CronTaskRegistrar SchedulingRunnable . 添加定时任务注册类&#xff0c;用来增加、删除定时任务 impo…

LLMs的星辰大海:大语言模型的前世今生

文章目录 一. LLM 的演进&#xff1a;从规则到智能的跃迁 &#x1f4ab;1.1 语言模型的蹒跚起步 &#x1f476;1.2 RNN 与 LSTM&#xff1a;序列建模的尝试 &#x1f9d0;1.3 Transformer 的横空出世&#xff1a;自注意力机制的革命 &#x1f4a5;1.4 LLM &#xff1a;从预测到…

路由器旁挂三层网络实现SDWAN互联(爱快SD-WAN)

近期因公司新办公区建设&#xff0c;原有的爱快路由器的SDWAN功能实现分支之间互联的服务还需要继续使用。在原有的小型网络中&#xff0c;使用的爱快路由器当作网关设备&#xff0c;所以使用较为简单,如下图所示。 现变更网络拓扑为三层网络架构&#xff0c;但原有的SDWAN分支…

flutter_学习记录_00_环境搭建

1.参考文档 Mac端Flutter的环境配置看这一篇就够了 flutter的中文官方文档 2. 本人环境搭建的背景 本人的电脑的是Mac的&#xff0c;iOS开发&#xff0c;所以iOS开发环境本身是可用的&#xff1b;外加Mac电脑本身就会配置Java的环境。所以&#xff0c;后面剩下的就是&#x…

15_业务系统基类

创建脚本 SystemRoot.cs 因为 业务系统基类的子类 会涉及资源加载服务层ResSvc.cs 和 音乐播放服务层AudioSvc.cs 所以在业务系统基类 提取引用资源加载服务层ResSvc.cs 和 音乐播放服务层AudioSvc.cs 并调用单例初始化 using UnityEngine; // 功能 : 业务系统基类 public c…

docker 安装 redis 详解

在平常的开发工作中&#xff0c;我们经常会用到 redis&#xff0c;那么 docker 下应该如何安装 redis 呢&#xff1f;简单来说&#xff1a;第一步&#xff1a;拉取redis镜像&#xff1b;第二步&#xff1a;设置 redis.conf 配置文件&#xff1b;第三步&#xff1a;编写 docker-…

困境如雾路难寻,心若清明步自轻---2024年创作回顾

文章目录 前言博客创作回顾第一次被催更第一次获得证书周榜几篇博客互动最多的最满意的引发思考的 写博契机 碎碎念时也运也部分经验 尾 前言 今年三月份&#xff0c;我已写下一篇《近一年多个人总结》&#xff0c;当时还没开始写博客。四月份写博后&#xff0c;就顺手将那篇总…

综合与时序分析的设计约束(1)——静态时序分析简介

目录 1.绪论2.静态时序分析与动态时序分析3.时序约束在静态时序分析中的作用3.1.约束作为声明3.2. 约束作为断言3.3.约束作为指令3.4.约束作为异常3.5.约束的角色变化 4.STA需要正确约束5.时序路径起点和终点6.建立与保持6.1 建立时间6.2 保持时间6.3 裕度 7.SDC主要类型7.1 时…

【算法日记】从零开始认识动态规划(一)

挫折会来也会过去&#xff0c; 热泪会流下也会收起&#xff0c; 没有什么可以让我气馁的&#xff0c; 因为&#xff0c;我有着长长的一生。 --- 席慕蓉 《写给幸福》--- 从零开始认识动态规划 1 动态规划问题1.1 什么是动态规划算法1.2 动态规划算法如何Debug1.3 动态规划…

八股学习 微服务篇

微服务篇 常见面试内容Spring Cloud 常见组件注册中心Ribbon负载均衡策略服务雪崩 常见面试内容 Spring Cloud 常见组件 Spring Cloud有5个常见组件&#xff1a; Eureka/Nacos:注册中心&#xff1b;Ribbon:负载均衡&#xff1b;Feign:远程调用&#xff1b;Hystrix/Sentinel:服…

【矢量数据】2024年最新中国省市县乡四级矢量地图数据 [推广有奖]

中国四级矢量地图数据是当前地理信息系统&#xff08;GIS&#xff09;中广泛应用的重要资源&#xff0c;涉及国家级、省级、市级、县级及乡级行政区的空间信息。这些数据可应用于地图绘制、城市规划、政府决策及各类地理分析等领域 一、中国四级矢量地图数据的介绍 本分享数据…

力扣707题(2)——设计链表

#题目 #3,5和6的代码 今天看剩下几个题的代码&#xff0c;1,2,4的代码已经在上篇博客写过了想看的小伙伴移步到&#xff1a; 力扣707题——设计链表-CSDN博客 //第3题头插法 void addAtHead(int val){ //记录头结点ListNode nhead; //新节点的创建,并让它指向原本头结点的后…

JavaWeb 学习笔记 XML 和 Json 篇 | 020

今日推荐语 愿你遇见好天气,愿你的征途铺满了星星——圣埃克苏佩里 日期 学习内容 打卡编号2025年01月23日JavaWeb笔记 XML 和 Json 篇020 前言 哈喽&#xff0c;我是菜鸟阿康。 以下是我的学习笔记&#xff0c;既做打卡也做分享&#xff0c;希望对你也有所帮助…