数据结构(复杂度)

复杂度

算法在编写成可执行程序后,运⾏时需要耗费时间资源和空间(内存)资源。因此衡量⼀个算法的好 坏,⼀般是从时间和空间两个维度来衡量的,即时间复杂度空间复杂度

时间复杂度主要衡量⼀个算法的运⾏快慢,⽽空间复杂度主要衡量⼀个算法运⾏所需要的额外空间。

在计算机发展的早期,计算机的存储容量很⼩。所以对空间复杂度很是在乎。但是经过计算机⾏业的迅速发展,计算机的存储容量已经达到了很⾼的程度。所以我们如今已经不需要再特别关注⼀个算法的空间复杂度。

时间复杂度

定义:在计算机科学中,算法的时间复杂度是⼀个函数式T(N),它定量描述了该算法的运⾏时间。时间复杂度是衡量程序的时间效率,那么为什么不去计算程序的运⾏时间呢?

1. 因为程序运⾏时间和编译环境和运⾏机器的配置都有关系,⽐如同⼀个算法程序,⽤⼀个⽼编译 器进⾏编译和新编译器编译,在同样机器下运⾏时间不同。

2. 同⼀个算法程序,⽤⼀个⽼低配置机器和新⾼配置机器,运⾏时间也不同。

3. 并且时间只能程序写好后测试,不能写程序前通过理论思想计算评估。

下面我们来看个案例:

// 请计算⼀下Func1中++count语句总共执⾏了多少
次? 
void Func1(int N) 
{ 
 int count = 0; 
 for (int i = 0; i < N ; ++ i) 
 { 
 for (int j = 0; j < N ; ++ j) 
 { 
 ++count; 
 } 
 } 
 for (int k = 0; k < 2 * N ; ++ k) 
 { 
 ++count; 
 } 
 int M = 10; 
 while (M--) 
 { 
 ++count; 
 } 
}

Func1执⾏的基本操作次数:

T (N) = N^2+2∗N+10

• N=10   T(N)=130

• N=100  T(N)=10210 

• N=1000  T(N)=1002010 

通过对N取值分析,对结果影响最⼤的⼀项是N^2。

实际中我们计算时间复杂度时,计算的也不是程序的精确的执⾏次数,精确执⾏次数计算起来还是很⿇烦的(不同的⼀句程序代码,编译出的指令条数都是不⼀样的),计算出精确的执⾏次数意义也不⼤, 因为我么计算时间复杂度只是想⽐较算法程序的增⻓量级,也就是当N不断变⼤时T(N)的差别,上⾯我们已经看到了当N不断变⼤时常数和低阶项对结果的影响很⼩,所以我们只需要计算程序能代表增⻓量级的⼤概执⾏次数,复杂度的表⽰通常使⽤⼤O的渐进表⽰法

大O渐进表示法

⼤O符号(Big O notation):是⽤于描述函数渐进⾏为的数学符号。

推导大O阶的规则:

1. 时间复杂度函数式T(N)中,只保留最⾼阶项,去掉那些低阶项,因为当N不断变⼤时,低阶项对结果影响越来越⼩,当N⽆穷⼤时,就可以忽略不计了。

2. 如果最⾼阶项存在且不是1,则去除这个项⽬的常数系数,因为当N不断变⼤,这个系数对结果影响越来越⼩,当N⽆穷⼤时,就可以忽略不计了。

3. T(N)中如果没有N相关的项⽬,只有常数项,⽤常数1取代所有加法常数。

通过以上⽅法,可以得到 Func1 的时间复杂度为:O(N^2)

时间复杂度案例分析

案例一: 

// 计算Func2的时间复杂度? 
void Func2(int N) 
{ 
 int count = 0; 
 for (int k = 0; k < 2 * N ; ++ k) 
 { 
 ++count; 
 } 
 int M = 10; 
 while (M--) 
 { 
 ++count; 
 } 
 printf("%d\n", count); 
} 

 Func2执⾏的基本操作次数

F(N) = 2N + 10

根据推导规则第3条得出

Func2的时间复杂度为:O(N)。

案例二:

// 计算Func3的时间复杂度? 
void Func3(int N, int M) 
{ 
 int count = 0; 
 for (int k = 0; k < M; ++ k) 
 { 
 ++count; 
 } 
 for (int k = 0; k < N ; ++ 
k) 
 { 
 ++count; 
 } 
 printf("%d\n", count); 
}

Func3执⾏的基本操作次数

F (N) = M + N 

因此:Func3的时间复杂度为:O(M+N)

案例三:

// 计算Func4的时间复杂度? 
void Func4(int N) 
{ 
 int count = 0; 
 for (int k = 0; k < 100; ++ k) 
 { 
 ++count; 
 } 
 printf("%d\n", count); 
}

Func4执⾏的基本操作次数

F (N) = 100

根据推导规则第1条得出

Func4的时间复杂度为O(1)

案例四:

// 计算strchr的时间复杂度? 
const char * strchr ( const char 
* str, int character)
{
 const char* p_begin = s;
 while (*p_begin != character)
 {
 if (*p_begin == '\0')
 return NULL;
 p_begin++;
}
 return p_begin;
}

strchr执⾏的基本操作次数

1)若要查找的字符在字符串第⼀个位置,则:F (N) = 1

2)若要查找的字符在字符串最后的⼀个位置, 则:F (N) = N 

3)若要查找的字符在字符串中间位置,则:F (N) = N/2 

因此:strchr的时间复杂度分为:

最好情况:O(1)

最坏情况:O(N)

平均情况:O(N)

小结:

通过上⾯我们会发现,有些算法的时间复杂度存在最好、平均和最坏情况。

最坏情况:任意输⼊规模的最⼤运⾏次数(上界)

平均情况:任意输⼊规模的期望运⾏次数

最好情况:任意输⼊规模的最⼩运⾏次数(下界)

⼤O的渐进表⽰法在实际中⼀般情况关注的是算法的上界,也就是最坏运⾏情况。


案例五:

// 计算BubbleSort的时间复杂度? 
void BubbleSort(int* a, int n) 
{ 
 assert(a); 
 for (size_t end = n; end > 0; --end) 
 { 
 int exchange = 0; 
 for (size_t i = 1; i < end; ++i) 
 { 
 if (a[i-1] > a[i]) 
 { 
 Swap(&a[i-1], &a[i]); 
 exchange = 1; 
 } 
 } 
 if (exchange == 0) 
 break; 
 } 
}

BubbleSort执⾏的基本操作次数

1)若数组有序,则:F (N) = N 

2)若数组有序且为降序,则:F (N) = (N∗(N + 1))/2

3)若要查找的字符在字符串中间位置,则为2)中情况的1/2.

因此:BubbleSort的时间复杂度取最差情况为:O(N^2).

案例六:

void func5(int n)
{
 int cnt = 1;
 while (cnt < n)
 {
 cnt *= 2;
 }
}

当n=2时,执⾏次数为1

当n=4时,执⾏次数为2

当n=16时,执⾏次数为4

假设执⾏次数为x ,则2^x = n

因此执⾏次数:x = log n

因此:func5的时间复杂度取最差情况为: O(log2 n)

*特别的,当n接近⽆穷⼤时,底数的⼤⼩对结果影响不⼤。因此,⼀般情况下不管底数是多少都可以省略不写,即可以表⽰为log n 。

案例七:

// 计算阶乘递归Fac的时间复杂度? 
long long Fac(size_t N) 
{ 
 if(0 == N) 
 return 1; 
 return Fac(N-1)*N; 
}

调⽤⼀次Fac函数的时间复杂度为O(1)

⽽在Fac函数中,存在n次递归调⽤Fac函数

因此:

阶乘递归的时间复杂度为:O(n)。


空间复杂度

空间复杂度也是⼀个数学表达式,是对⼀个算法在运⾏过程中因为算法的需要额外临时开辟的空间。空间复杂度不是程序占⽤了多少bytes的空间,因为常规情况每个对象⼤⼩差异不会很⼤,所以空间复杂度算的是变量的个数。

空间复杂度计算规则基本跟实践复杂度类似,也使⽤⼤O渐进表⽰法。

注意:函数运⾏时所需要的栈空间(存储参数、局部变量、⼀些寄存器信息等)在编译期间已经确定好了,因此空间复杂度主要通过函数在运⾏时候显式申请的额外空间来确定。

空间复杂度的计算示例

示例一:

// 计算BubbleSort的时间复杂度? 
void BubbleSort(int* a, int n) 
{ 
 assert(a); 
 for (size_t end = n; end > 0; --end) 
 { 
 int exchange = 0; 
 for (size_t i = 1; i < end; ++i) 
 { 
 if (a[i-1] > a[i]) 
 { 
 Swap(&a[i-1], &a[i]); 
 exchange = 1; 
 } 
 } 
 if (exchange == 0) 
 break; 
 } 
}

函数栈帧在编译期间已经确定好了, 只需要关注函数在运⾏时额外申请的空间。

BubbleSort额外申请的空间有exchange等有限个局部变量,使⽤了常数个额外空间

因此空间复杂度为:O(1)

示例二:

// 计算阶乘递归Fac的空间复杂度? 
long long Fac(size_t N) 
{ 
 if(N == 0) 
 return 1; 
 
 return Fac(N-1)*N; 
}

Fac递归调⽤了N次,额外开辟了N个函数栈帧, 每个栈帧使⽤了常数个空间

因此空间复杂度为:O(N)


常见复杂度对比

小结

以上便是我对复杂度的分享,不是高手,但是有颗成为高手的心态,欢迎各位在评论区提出问题,纠正不足。感谢观看!

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

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

相关文章

VsCode 与远程服务器 ssh免密登录

首先配置信息 加入下列信息 Host qb-zn HostName 8.1xxx.2xx.3xx User root ForwardAgent yes Port 22 IdentityFile ~/.ssh/id_rsa 找到自己的公钥&#xff0c;不带pub是私钥&#xff0c;打死都不能给别人。复制公钥 拿到公钥后&#xff0c;来到远程服务器 vim ~/.ss…

vue的学习--day1

一、软件的安装 首先&#xff0c;安装vscode,这个安装好像没有什么需要注意的点&#xff0c;如果不放心的话就网上找个博客&#xff0c;跟着步骤安装即可。 安装完成之后&#xff0c;在组件&#xff08;下图&#xff09;中安装相应的插件。首先建议英文和我一样不好的&#x…

buuctf-web

先输入127.0.0.1查找本地 得到网页目录&#xff0c;再输入127.0.0.1|ls查找下一级 得到php文件&#xff0c;127.0.0.1 | ls /返回上级目录 127.0.0.1 | cat /flag得到flag

【ARMv8/v9 异常模型入门及渐进 9.1 - FIQ 和 IRQ 打开和关闭】

请阅读【ARMv8/v9 ARM64 System Exception】 文章目录 FIQ/IRQ Enable and Disable汇编指令详解功能解释使用场景和注意事项 FIQ/IRQ Enable and Disable 在ARMv8/v9架构中&#xff0c;可以使用下面汇编指令来打开FIQ和 IRQ,代码如下&#xff1a; asm volatile ("msr da…

[Linux]CentOS软件的安装

一、Linux 软件包管理器 yum 1.Linux安装软件的方式 在linux中安装软件常用的有三种方式&#xff1a; 源代码安装&#xff08;我们还需要进行编译运行后才可以&#xff0c;很麻烦&#xff09; rpm安装&#xff08;Linux的安装包&#xff0c;需要下载一些rpm包&#xff0c;但是…

怎么把自己写的组件发布到npm官方仓库??

一.注册npm账号 npm官网 1.注册npm 账号 2.登陆 3.登陆成功 二.搭建一个vue 项目 具体步骤参考liu.z Z 博客 或者初始化一个vue项目 vue create XXX &#xff08;工程名字&#xff09;运行代码 npm run serve三.组件封装 1.在src文件下建一个package文件&#xff0…

力扣 二叉树 相关题目总结3

目录 一、 222 完全二叉树的节点个数 题目 题解 方法一&#xff1a;递归法 方法二&#xff1a; 二、257 二叉树的所有路径 题目 题解 方法一&#xff1a;递归法 方法二&#xff1a;回溯法 三、04 左叶子之和 题目 题解 一、 222 完全二叉树的节点个数 题目 给你…

元服务体验-服务发现

服务发现&#xff0c;无论线上或线下的方式都可以发现元服务。 线上&#xff1a;基于用户意图。从精准意图的搜索、用户事件触发的推荐到主动探索等场景。用户可以在设备的负一屏、全局搜索、应用市场、桌面等场景发现元服务。 线下&#xff1a;用户在 HarmonyOS Connect标签…

网络安全 DVWA通关指南 DVWA Brute Force (爆破)

DVWA Brute Force (爆破) 文章目录 DVWA Brute Force (爆破)LowMediumHighImpossible Low 1、分析网页源代码 <?php// 检查是否存在"Login" GET 参数&#xff0c;这通常是提交登录表单后触发的动作 if( isset( $_GET[ Login ] ) ) {// 获取POST方式提交的用户名…

rancher单节点安装k8s

k3s 优点: 可用性 易于操作的轻量级部署模型 缺点: 与上游Kubernetes不同 RKE1 优点: 与上游Kubernetes紧密对齐 缺点: 严重依赖于 Docker RKE2 凭借 k3s 的优势和更紧密的上游协调&#xff0c;RKE2 将控制平面组件作为静态 pod 启动&#xff0c;由 kubelet 管理。 为了符合行业…

Gitlab CI/CD --- use a sample CI/CD template

0 Preface/Foreword Pipeline, job, stage的关系如下描述&#xff1a; A pipeline is composed of independent jobs that run scripts, grouped into stages. Stages run in sequential order, but jobs within stages run in parallel. 关键信息&#xff1a; pipeline由独…

前端路由手写Hash和History两种模式

文章目录 1. Hash模式&#xff1a;简洁而广泛适用2. History模式&#xff1a;更自然的用户体验3. 结论 在现代Web开发中&#xff0c;单页面应用&#xff08;Single Page Application&#xff0c;简称SPA&#xff09;因其流畅的用户体验和高效的页面交互能力而备受青睐。前端路由…

51单片机STC89C52RC——19.1 SG90舵机(伺服电机)

目的/效果 独立按键K1&#xff0c;K2 实现加舵机减角度增减&#xff0c;LCD1602显示舵机转角度数&#xff08;上电默认90度&#xff09; 一&#xff0c;STC单片机模块 二&#xff0c;SG90舵机 2.1 简介 舵机只是我们通俗的叫法&#xff0c;它的本质是一个伺服电机&#xf…

LeetCode-返回链表倒数第K个节点、链表的回文结构,相交链表

一、返回链表倒数第k个节点 . - 力扣&#xff08;LeetCode&#xff09; 本体思路参展寻找中间节点的方法&#xff0c;寻找中间节点是定义快慢指针&#xff0c;快指针每次走两步&#xff0c;慢指针每次走一步&#xff0c;当快指针为空或者快指针的下一个节点是空时&#xff0c;…

maven项目容器化运行之2-maven中使用docker插件调用远程docker构建服务并在1Panel中运行

一.背景 公司主机管理小组的同事期望我们开发的maven项目能够在1Panel管理的docker容器部署。上一篇写了先开放1Panel中docker镜像构建能力maven项目容器化运行之1-基于1Panel软件将docker镜像构建能力分享给局域网-CSDN博客。这一篇就是演示maven工程的镜像构建、容器运行、运…

TCP/IP地址管理

TCP/IP中使用IP地址来确定网络上的一台主机 IPV4协议中&#xff0c;32位&#xff08;4个字节&#xff09;源IP地址和32位目的IP地址&#xff0c;也就是可以表示2^3242亿9千万个地址 如今随着互联网甚至物联网的迅速发展&#xff0c;我们面临着IP地址数量不充足的问题&#xf…

一款IM即时通讯聊天系统源码,包含app和后台源码

一款IM即时通讯聊天系统源码 聊天APP 附APP&#xff0c;后端是基于spring boot开发的。 这是一款独立服务器部署的即时通讯解决方案&#xff0c;可以帮助你快速拥有一套自己的移动社交、 企业办公、多功能业务产品。可以 独立部署&#xff01;加密通道&#xff01;牢牢掌握通…

云备份服务端

文件使用工具和json序列化反序列化工具 //文件和json工具类的设计实现 #ifndef __UTIL__ #define __UTIL__ #include<iostream> #include<fstream> #include<string> #include <vector> #include<sys/stat.h> #include"bundle.h" #inc…

热门软件缺陷管理工具2024:专业评测与建议

国内外主流的10款软件缺陷管理工具软件对比&#xff1a;PingCode、Worktile、禅道、Tapd、Teambition、Tower、JIRA、Bugzilla、MantisBT、Trac。 在软件开发过程中&#xff0c;管理缺陷和漏洞常常成为一项挑战&#xff0c;尤其是在项目规模庞大时。选择一个高效的软件缺陷管理…

【Linux环境sqlite下载安装教程】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 一、下载路径二、安装步骤 一、下载路径 https://sqlite.org/download.html 选择Alternative Source Code Formats下的sqlite-src-3460000.zip进行下载。 二、安…