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

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

前言

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

在这里插入图片描述

算法复杂度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 = 0for 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) - 1while left <= right:mid = (left + right) // 2if arr[mid] == target:return midelse if arr[mid] < target:left = mid + 1else:right = mid - 1return -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 = 0for 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 1else: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) // 2L = arr[:mid]R = arr[mid:]merge_sort(L)merge_sort(R)i = j = k = 0while i < len(L) and j < len(R):if L[i] < R[j]:arr[k] = L[i]i += 1else:arr[k] = R[j]j += 1k += 1while i < len(L):arr[k] = L[i]i += 1k += 1while j < len(R):arr[k] = R[j]j += 1k += 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),这两个复杂度是最为常见的。

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

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

私有仓库搭建

目前市面上比较常见的私有仓库搭建方法为&#xff1a; 通过 Sinopia 或 verdaccio 搭建&#xff08;Sinopia 已经停止维护&#xff0c;verdaccio 是 Fork 自 Sinopia&#xff0c;基本上大同小异&#xff09;&#xff0c;其优点是搭建简单&#xff0c;不需要其他服务。通过 cnp…

代码随想录--哈希表--两数之和

题目 给定一个整数数组 nums 和一个目标值 target&#xff0c;请你在该数组中找出和为目标值的那 两个 整数&#xff0c;并返回他们的数组下标。 你可以假设每种输入只会对应一个答案。但是&#xff0c;数组中同一个元素不能使用两遍。 示例: 给定 nums [2, 7, 11, 15], t…

【JAVA SE】抽象类和接口

✨✨欢迎大家来到Celia的博客✨✨ &#x1f389;&#x1f389;创作不易&#xff0c;请点赞关注&#xff0c;多多支持哦&#x1f389;&#x1f389; 所属专栏&#xff1a;JAVA 个人主页&#xff1a;Celias blog~ 目录 引言 一、抽象类 1.1 抽象类的定义 1.2 抽象方法 1.3 抽象…

【vue实战项目】通用管理系统:作业列表

目录 目录 1.前言 2.后端API 3.前端API 4.组件 5.分页 6.封装组件 1.前言 本文是博主前端Vue实战系列中的一篇文章&#xff0c;本系列将会带大家一起从0开始一步步完整的做完一个小项目&#xff0c;让你找到Vue实战的技巧和感觉。 专栏地址&#xff1a; https://blog…

springboot 实现kafka多源配置

文章目录 背景核心配置自动化配置类注册生产者、消费者核心bean到spring配置spring.factoriesyml配置使用 源码仓库 背景 实际开发中&#xff0c;不同的topic可能来自不同的集群&#xff0c;所以就需要配置不同的kafka数据源&#xff0c;基于springboot自动配置的思想&#xf…

Centos 7下的VulFocus靶场搭建详细教程

一、靶场介绍 自带 Flag 功能&#xff1a;每次启动 flag 都会自动更新&#xff0c;明确漏洞是否利用成功。带有计分功能。兼容 Vulhub、Vulapps 中所有漏洞镜像。 二、下载安装 下载 VMware 软件下载 centos镜像 三、Docker知识 学习链接&#xff1a;https://www.runoob.c…

如何在路由器上安装代理服务:详细教程

如何在路由器上安装代理服务&#xff1a;详细教程 步骤一&#xff1a;通过漏洞进入路由器系统开启Telnet服务使用Telnet登录路由器系统查看系统信息和CPU信息步骤二&#xff1a;交叉编译MIPS程序 Go对MIPS的支持 安装TFTP Server使用BusyBox tftp传输文件在路由器系统中下载编译…

linux进程加载和启动过程分析

我们的源代码通过预处理,编译,汇编,链接后形成可执行文件,那么当我们在终端敲下指令$ ./a.out argv1 argv2 后,操作系统是怎么将我们的可执行文件加载并运行的呢? 首先知道,计算机的操作系统的启动程序是写死在硬件上的,每次计算机上电时,都将自动加载启动程序,之后…

四、.Net8对接Ollama实现文字翻译(.Net8+SemanticKernel+Ollama)本地运行自己的大模型

.Net8SemanticKernelOllama 一、Semantic Kernel官方定义SK能做什么&#xff1f; 二、基本使用1、普通对话2、使用插件实现文本翻译功能 三、IChatCompletionService、ITextGenerationService、ITextEmbeddingGenerationService 很多情况都有这样的需求&#xff0c;使用自有系统…

运筹学_5.动态规划

文章目录 引言5.1 动态规划的基本概念首先明确什么是多阶段决策问题 5.2 动态规划的最短路径问题贝尔曼最优化原理建立动态规划模型的步骤 5.3 动态规划的投资分配问题投资分配问题定义投资分配问题的数学模型 5.4 动态规划的背包问题背包问题定义背包问题数学模型 引言 动态规…

Java——二进制原码、反码和补码

一、简要介绍 原码、反码和补码只是三种二进制不同的表示形式&#xff0c;每个二进制数都有这三个形式。 1、原码 原码是将一个数的符号位和数值位分别表示的方法。 最高位为符号位&#xff0c;0表示正&#xff0c;1表示负&#xff0c;其余位表示数值的绝对值。 例如&…

前端JS必用工具【js-tool-big-box】学习,检测密码强度

js-tool-big-box 前端工具库&#xff0c;实用的公共方法越来越多了&#xff0c;这一小节&#xff0c;我们带来的是检测密码强度。 我们在日常开发中&#xff0c;为了便于测试&#xff0c;自己总是想一个简单的密码&#xff0c;赶紧输入。但到了正式环境&#xff0c;我们都应该…

智能售货机的小投入大回报创业机遇

智能售货机的小投入大回报创业机遇 在当今这个快速进化的数字时代&#xff0c;智能售货机作为零售领域的新秀&#xff0c;正以其独特的便捷性和创新性逐步重塑传统零售格局。24小时不间断服务与自动化管理的结合&#xff0c;大幅度削减人力成本&#xff0c;使得智能售货机成为…

电脑突然提示:“failed to load steamui.dll”是什么情况?分享几种解决steamui.dll丢失的方法

相信有一些用户正在面临一个叫做“failed to load steamui.dll”的问题&#xff0c;这种情况多半发生在试图运行某个程序时&#xff0c;系统会提示一条错误消息&#xff1a;“failed to load steamui.dll”。那么&#xff0c;为何steamui.dll文件会丢失&#xff0c;又应该如何解…

Linux 使用 yum安装 ELK服务,yum 安装elasticsearch和Kibana(未写完)

文章目录 环境准备ELK组件介绍安装Elasticsearch安装Kibana 丢弃下载ELK 服务安装包Elasticsearch安装 Tips:关闭elasticsearch https修改 es 启动内存 环境准备 ELK组件介绍 ElasticSearch &#xff1a; 是一个近实时&#xff08;NRT&#xff09;的分布式搜索和分析引擎&…

Unity + 雷达 粒子互动(待更新)

效果预览: 花海(带移动方向) VFX 实例 脚本示例 使用TouchScript,计算玩家是否移动,且计算移动方向 using System.Collections; using System.Collections.Generic; using TouchScript; using TouchScript.Pointers; using UnityEngine; using UnityEngine.VFX;public …

python中的while循环

没有循环时&#xff0c;想打印0-100之间的数字&#xff0c;则需要循环多次&#xff0c;例&#xff1a; print(0) print(1) print(2) print(3) ... print(99) 但是使用循环的话&#xff0c;就不会有那么麻烦 while 循环 while 这个单词有“在……时”的含义&#xff0c;whil…

AI智能分析技术与安防视频融合当前面临的困难与挑战

人工智能与安防视频的融合为现代安全领域带来了革命性的变化&#xff0c;提高了安全管理水平、降低了管理成本并为用户提供了更加便捷和高效的服务。随着技术的不断进步和应用场景的不断拓展&#xff0c;未来人工智能与安防的融合将展现出更加广阔的发展前景。然而&#xff0c;…

Linux 服务查询命令(包括 服务器、cpu、数据库、中间件)

Linux 服务查询命令&#xff08;包括 服务器、cpu、数据库、中间件&#xff09; Linux获取当前服务器ipLinux使用的是麒麟版本还是cenos版本Linux获取系统信息Linux查询nignx版本 Linux获取当前服务器ip hostname -ILinux使用的是麒麟版本还是cenos版本 这个文件通常包含有关L…

Vue进阶之Vue无代码可视化项目(三)

Vue无代码可视化项目 项目初始化store的使用DataSourceView.vuestores/counter.ts开发模式按钮store/editor.tsLayoutView.vue导航条安装图标iconpackage.jsonstore/debug.tssrc/components/AppNavigator.vueAppNavigator.ts:AppNavigator.vue:theme样式theme/reset.csstheme/v…