【数据结构】复杂度的重要性—–决定程序运行的效率

【数据结构】复杂度的重要性—–决定程序运行的效率

前言

在我们写算法的时候,常常会需要考虑一个问题:这个算法好不好?而这个“好”实际上就取决于是算法的复杂度。

在这里插入图片描述

算法复杂度Algorithmic Complexity)是指算法在编写成可执行程序后,运行时所需要的资源,资源包括时间资源和内存资源。应用于数学和计算机导论。

我们知道,同一个问题可以使用不同的算法来解决,而这里的不同一般来说也是可以从复杂度来看出的,当然,也不排除有相同复杂度但不同写法的算法,这里只作参考。

一个算法的好坏影响到了很多实际性的问题,在程序中效率是极其重要的,一个算法的评价主要从时间复杂度和空间复杂度来考虑。

在介绍这两个复杂度之前,我们需要了解一个概念:复杂度并不是具体的数值或者是大小表示,它实际上是一个量级的概念

比如O(n)和O(n+1)。很明显,它们是同一个量级,只差了1,那么它们实际上可以写成同一个,也就是O(n),甚至像O(2n),它和上面两者也是同一个量级;但是,像O(n)和O(n^2),这俩实际上并不属于同一个量级。它们之间隔了一个次方,那么就有可能存在大小的很大差距,所以这两个复杂度是两个不同的个体。

接下来对这两个复杂度进行具体介绍。

时间复杂度

基本定义和理解

时间复杂度衡量的是算法运行时间随输入规模的增长情况。

对于算法的运行时间,在实际中,由于每台计算机的硬件和软件环境的不同,往往不能精确计算执行所需时间。所以我们在讨论时间复杂度的时候,仅仅从理论角度,也就是视作计算机的环境不变,来进行讨论。

影响算法时间代价的具体有两个方面:问题规模语句频度

A.问题规模是算法求解问题输入量的多少,是问题大小的本质表示,一般用整数n表示。

B.语句频度是一条语句的重复执行次数。一般来说,这个次数会用一个函数来表示,而这个函数代表着算法执行时间的增长率算法执行时间的增长率称作渐进时间复杂度,也就简称为时间复杂度

时间复杂度通常使用O(n)来表示,算法的复杂度存在最好、最坏等情况,那么在这里我们取算法的最坏运行情况作为O(n)

在我们进行时间复杂度的解答时,实际上不需要这么复杂的解释。我们可以将其总结为一句话:

只分析算法中最耗时的操作。

由于我们考虑的是算法的最坏运行情况,并且是以量级来计算,那么我们实际上只需要分析算法中最耗时的操作即可。

分析步骤

1.确定输入规模 (n):输入规模通常是算法中主要变量的数量,例如数组的长度。

2.识别基本操作:确定算法中最耗时的操作,其他比较繁琐、或者特殊的语句忽视。

3.分析每部分的操作次数:计算基本操作在不同结构中的次数,例如循环、递归。

4.累加所有部分的操作次数:将各部分的操作次数加起来,得到总操作次数。

5.用大O符号表示:忽略常数和低阶项,提取时间复杂度的主要部分。这里的目的实际上就是统一量级。

使用这样的步骤,我们就可以较好地解决时间复杂度的分析了。

举例

示例1:简单循环

def sum_array(arr):
    total = 0
    for i in range(len(arr)):
        total += arr[i]
    return total

步骤1:确定输入规模

输入是数组 arr,其长度为 n

步骤2:识别基本操作

基本操作是加法 total += arr[i]

步骤3:分析每部分的操作次数

  • total = 0:1 次
  • for i in range(len(arr)):循环执行 n
  • total += arr[i]:循环体内执行 n

步骤4:累加所有部分的操作次数

总操作次数为 1+n+n=1+2n1 + n + n = 1 + 2n1+n+n=1+2n。

步骤5:用大O符号表示

忽略常数项和系数,时间复杂度为 O(n)。

示例2:嵌套循环

def bubble_sort(arr):
    n = len(arr)
    for i in range(n):
        for j in range(0, n-i-1):
            if arr[j] > arr[j+1]:
                arr[j], arr[j+1] = arr[j+1], arr[j]

步骤1:确定输入规模

输入是数组 arr,其长度为 n

步骤2:识别基本操作

基本操作是比较和交换 if arr[j] > arr[j+1]arr[j], arr[j+1] = arr[j+1], arr[j]

步骤3:分析每部分的操作次数

步骤4:累加所有部分的操作次数

分析这里的操作次数,我们可以使用更为简单的方法,请注意,这里的for循环中还嵌套了一个for循环,那么我们可以理解为:在进行大循环的时候,也会进行一次小循环,而小循环中的语句会进行n次,那么就是O(n);而大循环会进行n次小循环,那么总的时间复杂度就是O(n*n)也就是O(n^2)。

注意:遇见嵌套类的题目,我们都这样计算:嵌套中有几个循环,就是n的几次方。

步骤5:用大O符号表示

忽略常数项和系数,时间复杂度为 O(n^2)。

示例3:二分查找

def binary_search(arr, target):
    left, right = 0, len(arr) - 1
    while left <= right:
        mid = (left + right) // 2
        if arr[mid] == target:
            return mid
        else if arr[mid] < target:
            left = mid + 1
        else:
            right = mid - 1
    return -1

步骤1:确定输入规模

输入是数组 arr,其长度为 n

步骤2:识别基本操作

基本操作是比较 arr[mid] == target

步骤3:分析每部分的操作次数

  • left, right = 0, len(arr) - 1:1 次
  • while left <= right:循环次数由 leftright 的变化决定,每次循环减半,最多执行log2(n)次。

步骤4:累加所有部分的操作次数

总操作次数为 1+log2(n)

步骤5:用大O符号表示

忽略常数项和低阶项,时间复杂度为 O(log n)。

经过以上的介绍和举例,相信各位已经能够游刃有余地解决时间复杂度的问题了。

空间复杂度

基本定义和理解

与时间复杂度类似,空间复杂度是指算法在计算机内执行时所需存储空间的度量。当我们需要区别算法时,我们应该看到的是每个算法的不同处,由于输入的初始数据所占的存储空间是已经确定的,那么在计算空间复杂度时,我们往往只分析执行过程中所需要额外的存储空间大小。

分析步骤

1.确定输入规模 (n):输入规模通常是算法中主要变量的数量,例如数组的长度。

2.识别存储需求:确定算法中每个变量和数据结构所需的存储空间。

3.分析每部分的存储空间需求:计算不同部分占用的空间,如局部变量、数组、递归调用栈等。

4.累加所有部分的存储空间需求:将各部分的存储空间加起来,得到总的空间需求。

5.用大O符号表示:忽略常数和低阶项,提取空间复杂度的主要部分。

举例

示例1:简单循环

def sum_array(arr):
    total = 0
    for i in range(len(arr)):
        total += arr[i]
    return total

步骤1:确定输入规模

输入是数组 arr,其长度为 n

步骤2:识别存储需求

  • arr:长度为 n,空间需求为 O(n)
  • total:一个整数,空间需求为 O(1)
  • i:一个整数,空间需求为 O(1)

步骤3:分析每部分的存储空间需求

  • arr:O(n)
  • total:O(1)
  • i:O(1)

步骤4:累加所有部分的存储空间需求

总空间需求为 O(n)+O(1)+O(1)=O(n)。

步骤5:用大O符号表示

忽略常数项和低阶项,空间复杂度为 O(n)。

示例2:递归函数

def factorial(n):
    if n == 0:
        return 1
    else:
        return n * factorial(n-1)

步骤1:确定输入规模

输入是整数 n

步骤2:识别存储需求

  • 递归调用栈:每次递归调用会占用栈空间。

步骤3:分析每部分的存储空间需求

  • 每次递归调用占用 O(1) 空间,最多递归调用 n 次。

步骤4:累加所有部分的存储空间需求

总空间需求为 O(1)∗n=O(n)。

步骤5:用大O符号表示

忽略常数项和低阶项,空间复杂度为 O(n)。

示例3:使用辅助数组

def merge_sort(arr):
    if len(arr) > 1:
        mid = len(arr) // 2
        L = arr[:mid]
        R = arr[mid:]

        merge_sort(L)
        merge_sort(R)

        i = j = k = 0

        while i < len(L) and j < len(R):
            if L[i] < R[j]:
                arr[k] = L[i]
                i += 1
            else:
                arr[k] = R[j]
                j += 1
            k += 1

        while i < len(L):
            arr[k] = L[i]
            i += 1
            k += 1

        while j < len(R):
            arr[k] = R[j]
            j += 1
            k += 1

步骤1:确定输入规模

输入是数组 arr,其长度为 n

步骤2:识别存储需求

  • arr:长度为 n,空间需求为 O(n)
  • 辅助数组 LR:总长度为 n,空间需求为 O(n)

步骤3:分析每部分的存储空间需求

  • arr:O(n)
  • 辅助数组 LR:O(n)
  • 局部变量 i, j, k, mid:O(1)

步骤4:累加所有部分的存储空间需求

总空间需求为 O(n)+O(n)+O(1)=2O(n)+O(1)=O(n)。

步骤5:用大O符号表示

忽略常数项和低阶项,空间复杂度为 O(n)。

注意:在空间复杂度的计算中,大部分都是O(n)和O(1),这两个复杂度是最为常见的。

如何理解和应用复杂度分析

理解复杂度分析的核心是**能够评估算法在最坏、最好和平均情况下的性能。**这有助于我们在开发过程中选择最合适的算法和数据结构以确保程序的高效运行。

算法的高效性往往在算法指标中占据较高位置,所以只要我们使得复杂度越,高效性越,那么算法也就会越

的存储空间需求**

总空间需求为 O(n)+O(n)+O(1)=2O(n)+O(1)=O(n)。

步骤5:用大O符号表示

忽略常数项和低阶项,空间复杂度为 O(n)。

注意:在空间复杂度的计算中,大部分都是O(n)和O(1),这两个复杂度是最为常见的。

如何理解和应用复杂度分析

理解复杂度分析的核心是**能够评估算法在最坏、最好和平均情况下的性能。**这有助于我们在开发过程中选择最合适的算法和数据结构以确保程序的高效运行。

算法的高效性往往在算法指标中占据较高位置,所以只要我们使得复杂度越,高效性越,那么算法也就会越

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

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

相关文章

粒子系统技术在AI绘画中的创新应用

引言&#xff1a; 随着人工智能技术的飞速发展&#xff0c;AI绘画已经成为艺术创作和数字媒体领域的一大热点。粒子系统作为一种模拟复杂物理现象的技术手段&#xff0c;其在AI绘画中的应用为创作过程带来了前所未有的灵活性和创新性。本文将深入探讨粒子系统技术的原理、特点以…

Nvidia Jetson/Orin +FPGA+AI大算力边缘计算盒子:人工智能消防应用

青鸟消防股份有限公司成立于2001年6月&#xff0c;于2019年8月在深圳证券交易所挂牌上市&#xff0c;成为中国消防报警行业首家登陆A股的企业。公司始终聚焦于消防安全与物联网领域&#xff0c;主营业务为“一站式”消防安全系统产品的研发、生产和销售。公司产品已覆盖了火灾报…

【Linux 网络】高级 IO -- 详解

一、IO 的基本概念 I/O&#xff08;input/output&#xff09;也就是输入和输出&#xff0c;在冯诺依曼体系结构当中&#xff0c;将数据从输入设备拷贝到内存就叫作输入&#xff0c;将数据从内存拷贝到输出设备就叫作输出。 对文件进行的读写操作本质就是一种 IO&#xff0c;文…

近邻算法详解:原理、Java实现及应用场景

摘要 近邻算法&#xff08;Nearest Neighbor Algorithm&#xff09;是一类基于实例的学习方法&#xff0c;广泛应用于分类和回归问题中。最常见的近邻算法是K近邻算法&#xff08;K-Nearest Neighbors, KNN&#xff09;&#xff0c;其基本思想是通过计算待分类样本与训练样本的…

内网渗透-详解代理逻辑及隧道

写在前面 红蓝对抗过程中打点以后往往需要进行内网渗透和横向移动&#xff0c;因此大家都需要扎实掌握代理和隧道知识&#xff0c;一款优秀的代理工具也可以给内网渗透带来很大的收益。 1.正向代理&#xff1a; 代理客户端&#xff0c;帮助客户端完成所需请求。 举例&#x…

系统架构设计师【第6章】: 数据库设计基础知识 (核心总结)

文章目录 6.1 数据库基本概念6.1.1 数据库技术的发展6.1.2 数据模型6.1.3 数据库管理系统6.1.4 数据库三级模式 6.2 关系数据库6.2.1 关系数据库基本概念6.2.2 关系运算6.2.3 关系数据库设计基本理论 6.3 数据库设计6.3.1 数据库设计的基本步骤6.3.2 数据需求分析6…

梵几 x TapData:如何高效落地实时数据中台,助力家居企业优化数字营销

使用 TapData&#xff0c;化繁为简&#xff0c;摆脱手动搭建、维护数据管道的诸多烦扰&#xff0c;轻量代替 OGG、DSG 等同步工具&#xff0c;「CDC 流处理 数据集成」组合拳&#xff0c;加速数据流转&#xff0c;帮助企业将真正具有业务价值的数据作用到实处&#xff0c;将“…

【FISCO BCOS 3.0】一、新版本搭链介绍

目录 一、区块链种类的变化 二、搭链演示 1.单群组区块链&#xff08;Air版本&#xff09; 2.多群组区块链&#xff08;Pro版本&#xff09; 3.可扩展区块链&#xff08;Max版本&#xff09; FISCO BCOS的发展速度如日中天&#xff0c;对于稳定的2.0版本而言&#xff0c;偶…

【【手把手教你实现Risc-V装载至FPGA】】

RiscV实现教程 参考来源 tinyriscv: https://gitee.com/liangkangnan/tinyriscv 平台实现 &#xff1a; Linux ubuntu 实现介绍 环境 &#xff1a; 需要 iverilog (切换到 v11或以上的版本&#xff09; 1.下载iverilog源码 git clone https://github.com/steveicarus/iverilog.…

zookeeper启动(一)

1.zookeeper启动入口 在zkServer.sh的启动命令中,我们可以找到zookeeper启动的关键类org.apache.zookeeper.server.quorum.QuorumPeerMain QuorumPeerMain#main 我们可以直接看org.apache.zookeeper.server.quorum.QuorumPeerMain中的main方法,从下面的main方法中,我们可以…

收银系统源码-千呼新零售2.0【线上商城商品详情页细节优化】

千呼新零售2.0系统是零售行业连锁店一体化收银系统&#xff0c;包括线下收银线上商城连锁店管理ERP管理商品管理供应商管理会员营销等功能为一体&#xff0c;线上线下数据全部打通。 适用于商超、便利店、水果、生鲜、母婴、服装、零食、百货等连锁店使用。 详细介绍请查看下…

媳妇面试了一家公司,期望月薪20K,对方没多问就答应了,只要求3天内到岗,可我总觉得哪里不对劲。

“20k&#xff01;明天就来上班吧&#xff01;” 听到这句话&#xff0c;你会不会两眼放光&#xff0c;激动得差点跳起来&#xff1f; 朋友媳妇小丽&#xff0c;最近就经历了这样一场“梦幻面试”。然而&#xff0c;事情的发展却远没有想象中那么美好…… “这公司也太好了吧…

python udp双向通信

import json import socket import threading import loggingthislist [] thisneednum {}class ChatUdpMain:def __init__(self):#其他原有逻辑 begin#其他原有逻辑 end# 1.创建socket套接字 收self.udp_socket_receive socket.socket(socket.AF_INET, socket.SOCK_DGRAM…

Cocos入门2:软件安装

Cocos Creator的安装教程如下&#xff0c;按照步骤进行&#xff0c;可以帮助您顺利安装Cocos Creator&#xff1a; 一、下载Cocos Dashboard 访问Cocos官网&#xff1a;前往Cocos Creator的官方网站&#xff08;https://www.cocos.com/creator/&#xff09;。 下载Cocos Dash…

arco design表单label和输入框的空间分布

表单空间分布 arco利用的栅格系统来实现label、input的大小分布 <a-form :model"formData.form" :label-col-props"{ span: 6 }" :wrapper-col-props"{ span: 18 }" >// 其它...... </a-form>栅格系统中&#xff0c;默认空间总量2…

ES6-01-简介

一、什么是ES6&#xff1f; 每年一个版本o(╥﹏╥)o。 二、javaScript新特性的特点 1、语法简洁&#xff0c;功能丰富&#xff1b; 2、框架开发应用。 3、岗位需求&#xff01; 三、let关键字 3-1、声明变量 let a;let a,b;let e100;let f521, gmilk-love, h[]; 3-2、声明的…

6FC5357-0BB22-0AE0处理器CPU模块单价

6FC5357-0BB22-0AE0处理器CPU模块单价 6FC5357-0BB22-0AE0处理器CPU模块单价 6FC5357-0BB22-0AE0处理器CPU模块单价 6FC5357-0BB22-0AE0处理器CPU模块中文说明书 6FC5357-0BB22-0AE0处理器CPU模块工作原理图 商品编号(市售编号) 6FC5357-0BB22-0AE0 产品说明 ***备件**…

高中数学:解三角形-三角变换法

一、三角变换法 三角变换法&#xff0c;就是&#xff0c;在解三角形的过程中&#xff0c;将正弦定理和余弦定理结合两角和与差公式来解答题目 二、练习 例题1 解析 题目中的第二个等式&#xff0c;我们应该是一眼看出来&#xff0c;用余弦定理&#xff0c;求出cosA的值。从…

深入分析 Android BroadcastReceiver (二)

文章目录 深入分析 Android BroadcastReceiver (二)1. 深入理解 BroadcastReceiver 的高级使用和优化2. 有序广播&#xff08;Ordered Broadcasts&#xff09;2.1 实现有序广播 3. 粘性广播&#xff08;Sticky Broadcasts&#xff09;3.1 使用粘性广播 4. 本地广播&#xff08;…

国产QX320F280049,双核对比TI的 C28X+CLA,谁的处理能力更强

国产QX320F280049&#xff0c;独立双核32位CPU&#xff0c;主频150MHz 每个核都有TMU&#xff0c;FPU&#xff0c;VCU等运算 16个高分辨率HRPWM&#xff08;150PS&#xff09; 3个12位ADC&#xff0c;采样率3MSPS TI的 TMS320F280049采用C28XCLA协处理器模式&#xff0c;主频只…