【数据结构初阶】一. 复杂度讲解

=========================================================================

相关代码gitee自取

C语言学习日记: 加油努力 (gitee.com)

 =========================================================================

接上期

学C的第三十四天【程序环境和预处理】_高高的胖子的博客-CSDN博客

 =========================================================================

                     

1 . 算法效率

(1). 什么是数据结构:

               

数据结构(Data Structure)是计算机存储组织数据的方式

相互之间存在一种或多种特定关系的数据元素的集合

                     


                    

(2). 什么是算法:

                

算法(Algorithm)就是定义良好的计算过程

取一个或一组的值为输入,并产生出一个或一组值作为输出

简单来说算法就是一系列的计算步骤用来将输入数据转化成输出结果

                     


                    

(3). 算法的复杂度:

                     

算法编写成可执行程序后运行时需要耗费时间资源空间(内存)资源

因此衡量一个算法的好坏,一般是时间空间两个维度来衡量的,

时间复杂度空间复杂度

                      

时间复杂度主要衡量一个算法的运行快慢

空间复杂度主要衡量一个算法运行所需要的额外空间

计算机发展的早期计算机的存储容量很小。所以对空间复杂度很是在乎

但是经过计算机行业的迅速发展计算机的存储容量已经达到了很高的程度

所以我们如今已经不需要再特别关注一个算法的空间复杂度

               

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

                   

2 . 时间复杂度

(1). 时间复杂度的概念:

               

计算机科学中算法的时间复杂度是一个函数,它定量描述了该算法的运行时间

一个算法执行所耗费的时间,从理论上说,是不能算出来的,

只有把你的程序放在机器上跑起来才能知道

但是我们需要每个算法都上机测试吗?是可以都上机测试,但是这很麻烦

所以才有了时间复杂度这个分析方式

            

一个算法所花费的时间其中语句的执行次数成正比例

算法中的基本操作的执行次数,为算法的时间复杂度

                       

即:

找到某条基本语句问题规模N之间数学表达式,就是算出该算法的时间复杂度

             

图例:Func1执行的基本操作次数

                 

上图得到的Func1执行的基本次数为:

F(N) = N^2 + 2*N +10

但实际我们计算时间复杂度时,我们其实并不一定要计算精确的执行次数

只需要大概执行次数,那么这里我们使用大O的渐进表示法

                     


                    

(2). 大O的渐进表示法:

          

大O符号Big O notation):是用于描述函数渐进行为的数学符号

            

推导大O阶方法

1、常数1取代运行时间中的所有加法常数

2、在修改后的运行次数函数中,只保留最高阶项

3、如果最高阶项存在且不是1,则去除与这个项目相乘的常数得到的结果就是大O阶

            

使用大O的渐进表示法以后,

F(N) = N^2 + 2*N +10

第一步:+10 变为 +1

第二步:保留最高阶项 N^2

第三步:最高项相乘常数为1不用去除

            

所以Func1的时间复杂度O(N^2)

N = 10             F(N) = 100        

N = 100           F(N) = 10000    

N = 1000         F(N) = 1000000

通过上面我们会发现大O的渐进表示法去掉了那些对结果影响不大的项

简洁明了的表示出了执行次数

大O的渐进表示法本质计算的是算法属于哪个量级

           

另外有些算法的时间复杂度存在最好平均最坏情况

(可查看下方案例四)

最坏情况:任意输入规模的最大运行次数(上界)

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

最好情况:任意输入规模的最小运行次数(下界)

                

例如:在一个长度为N数组中搜索一个数据x

最坏情况N次找到

平均情况N/2次找到

最好情况1次找到

实际操作下一般情况关注的是算法的最坏运行情况

所以数组中搜索数据时间复杂度为O(N)

                     


                    

(3). 常见时间复杂度计算案例:

          

案例一:

//示例一:
//计算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);
}

图示:

               

               

案例二:

//示例二:
//计算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);
}

图示:

               

               

案例三:

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

图示:“cpu技术太强了”

               

               

案例四:

//示例四:
//计算strchr的时间复杂度:
const char* strchr(const char* str, int character);
//strchr库函数:在str字符数组中查找一个字符

图示:

               

               

案例五:

//示例五:
#include <stdio.h>
//计算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;}}
}

图示:

               

               

案例六:

//示例六:
//计算BinarySearch的时间复杂度:
int BinarySearch(int* a, int n, int x)
{assert(a);int begin = 0;int end = n - 1;// [begin, end]:begin和end是左闭右闭区间,因此有=号while (begin <= end){int mid = begin + ((end - begin) >> 1);if (a[mid] < x){begin = mid + 1;}else if (a[mid] > x){end = mid - 1;}else{return mid;}}return -1;
}

图示:

               

               

案例七:

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

图示:

               

               

案例八:

//示例八:
//计算斐波那契递归Fib的时间复杂度:
long long Fib(size_t N)
{if (N < 3){return 1;}return Fib(N - 1) + Fib(N - 2);
}

图示:

                     


                    

(4). 常见时间复杂度对比

             

一般算法常见的复杂度如下表:

5201314O(1)常数阶
3n + 4O(n)线性阶
3n^2 + 4n + 5O(n^2)平方阶
3log(2)n + 4O(logn)对数阶
2n + 3nlog(2)n + 14O(nlogn)nlogn阶
n^3 + 2n^2 + 4n + 6O(n^3)立方阶
2^nO(2^n)指数阶

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

3 . 空间复杂度

(1). 空间复杂度的概念:

                     

空间复杂度是一个数学表达式

对一个算法在运行过程中额外临时占用存储空间大小的量度

                

空间复杂度不是程序占用了多少bytes的空间,因为这个也没太大意义,

所以空间复杂度算的是变量的个数

空间复杂度计算规则基本跟时间复杂度类似也使用大O渐进表示法

                   

注意:

函数运行时所需要的栈空间(存储参数局部变量、一些寄存器信息等)

编译期间已经确定好了

因此空间复杂度主要通过函数在运行时候显式申请的额外空间来确定

                     


                    

(2). 常见空间复杂度计算案例:

           

案例一:

//计算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;}}
}

图示:

           

           

案例二:

//计算Fibonacci的空间复杂度:
//返回斐波那契数列的前n项
long long* Fibonacci(size_t n)
{if (n==0){return NULL;}long long* fibArray = (long long*)malloc((n + 1) * sizeof(long long));fibArray[0] = 0;fibArray[1] = 1;for (int i = 2; i <= n; ++i){fibArray[i] = fibArray[i - 1] + fibArray[i - 2];}return fibArray;
}

图示:

           

           

案例三:

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

图示:

         

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

             

4 . 复杂度的oj练习

(1). 时间复杂度练习:消失的数字

                   

对应链接:

面试题 17.04. 消失的数字 - 力扣(LeetCode)

              

题目:

           

解决思路一:使用等差数列公式

             

假设数组nums包含从0到n的所有整数

那么就可以使用 0+N等差公式 计算出一个结果

该结果等于 0~n的各数相加总和

用这个结果 减去 数组中的值

结果就是消失的数字的值

              

图示:

对应代码:
int missingNumber(int* nums, int numsSize){int N = numsSize;int ret = N*(N+1)/2;for(int i = 0; i < N; i++){ret -= nums[i];}return ret;
}

              

解决思路二:异或法

             

用 0 异或 完整的0~N各值

再用该异或的结果异或 nums数组少一个值

因为异或后相同为0相异为1

此时两对值中相同的值就会异或为0

nums少的一个值异或后就会得到该值

              

图示:

对应代码:
int missingNumber(int* nums, int numsSize){int N = numsSize;int x = 0; //用来保存异或后的结果for(int i = 0; i <= N; ++i){x ^= i;}for(int i = 0; i < N; ++i){x ^= nums[i];}return x;
}

                      


                    

(2). 空间复杂度练习:轮转数组

                   

对应链接:

189. 轮转数组 - 力扣(LeetCode)

               

题目:要求时间复杂度为O(N),空间复杂度为为O(1)

           

解决思路一:整体右旋

             

原数组分为两部分

假设需要右旋k个数字

以原数组末尾k个数字为一组剩下其他数字为一组

两组进行调换,即可实现

              

图示:

对应代码:
void rotate(int* nums, int numsSize, int k){//用空间换时间:int n = numsSize; //数组长度int* tmp = (int*)malloc(sizeof(int)*n);k %= n; //确保要右旋个数小于数组大小//直接使用memcpy函数进行调换:memcpy(tmp, nums+n-k, sizeof(int)*k); //把后k个值移到前面// tmp : 起始位置// nums+n-k : 数组nums后k个值的起始位置// sizeof(int)*k :拷贝k个int大小的数据memcpy(tmp+k, nums, sizeof(int)*(n-k)); //把后k个值移到前面// tmp+k : 拷贝到tmp+k的位置,因为上面把后k个值放在了前面// nums : 数组nums开始位置// sizeof(int)*(n-k) :拷贝(n-k)个int大小的数据//再赋给数组nums:memcpy(nums, tmp, sizeof(int)*n);//释放开辟的动态空间:free(tmp);
}

           

解决思路二:逆置

             

将原数组的前 n-k 个数逆置

后 k 个数也逆置

最后再整体逆置,即可实现

              

图示:

对应代码:
//逆置函数:
void reverse(int* a, int left, int right)
{while(left < right){int tmp = a[left];a[left] = a[right];a[right] = tmp;++left;--right;}
}void rotate(int* nums, int numsSize, int k){k %= numsSize;//逆置前 n-k 个数:reverse(nums, 0, numsSize-k-1);//逆置后 k 个数:reverse(nums, numsSize-k, numsSize-1);//最后整体逆置:reverse(nums, 0, numsSize-1);
}

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

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

相关文章

STM32G030F6 (SOP-20)Cortex ® -M0+, 32KB Flash, 8KB RAM, 17 GPIOs

淘宝淘了一批 STM32G030F6P6 SOP20&#xff0e;先备注一下, 还没想到能干嘛用&#xff0e; 手上的 STM32F103C6T6还剩一些&#xff0e; 一堆 “淘宝原厂STM32F103C8T6”, 还烫着手. 理解信息: ( 逐步补充 ) System Clock GPIOs GPIOs 17 PA[7:0] : 8bits USART Timer ADC I2…

git 操作

merge操作 git checkout -b bugFix git commit //在bugFix branch上操作&#xff0c;并且commit git checkout main git commit //在main上操作&#xff0c;并且commit git merge bugFix //此时on main branch&#xff0c;如果在bugFix branch执行merge bugFix&#…

springdoc-openapi-ui 整合 knife,多模块分组,脚手架

pom文件&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation"http://maven.apache.o…

混合查询多家快递,快速掌握物流信息

在现代社会&#xff0c;快递服务已成为我们日常生活的重要组成部分。无论是购物还是文件传递&#xff0c;我们都需要快递服务的帮助。然而&#xff0c;不同的快递公司需要不同的查询方法&#xff0c;这无疑增加了我们的查询难度。因此&#xff0c;有没有一种方法可以让我们一次…

CVE-2023-3450:锐捷 RG-BCR860 命令执行漏洞复现

锐捷 RG-BCR860 命令执行漏洞(CVE-2023-3450)复现 0x01 前言 本次测试仅供学习使用&#xff0c;如若非法他用&#xff0c;与本文作者无关&#xff0c;需自行负责&#xff01;&#xff01;&#xff01; 0x02 漏洞描述 Ruijie Networks RG-BCR860是中国锐捷网络&#xff08;R…

【英文文章总结】数据管理指南系列:渐进式数据库设计

原文连接&#xff1a;系列https://martinfowler.com/data/https://martinfowler.com/data/ Evolutionary Database Design (martinfowler.com) 架构的进化。如何允许更改架构并轻松迁移现有数据&#xff1f; 如何应对项目的变动 迭代开发&#xff1a;很长一段时间人们把数据…

如何高效的解析Json?

Json介绍 Json是一种数据格式&#xff0c;广泛应用在需要数据交互的场景Json由键值对组成每一个键值对的key是字符串类型每一个键值对的value是值类型(boo1值数字值字符串值)Array类型object类型Json灵活性他可以不断嵌套&#xff0c;数组的每个元素还可以是数组或者键值对键值…

HDFS 集群动态节点管理

目录 一、动态扩容、节点上线 1.1 背景 1.2 扩容步骤 1.2.1 新机器基础环境准备 1.2.2 Hadoop 配置 1.2.3 手动启动 DataNode 进程 1.2.4 Web 页面查看情况 1.2.5 DataNode 负载均衡服务 二、动态缩容、节点下线 2.1 背景 2.2 缩容步骤 2.2.1 添加退役节点 …

/etc/ssh/sshd_config 配置文件中的 PasswordAuthentication PermitRootLogin 参数作用

1、PasswordAuthentication PasswordAuthentications是一种身份验证方式&#xff0c;通常用于远程服务器的登录。当用户连接到远程服务器并进行身份验证时&#xff0c;服务器会根据 用户名进行验证&#xff0c;如果验证成功允许用户访问服务器。在SSH中&#xff0c;PasswordAut…

centos7上hive3.1.3安装及配置

1、安装背景&#xff1b; hive是基于hadoop的数据仓库软件&#xff0c;部署运行在linux系统之上&#xff0c;安装之前必须保证hadoop环境运行正常&#xff0c;hive本身不是分布式软件&#xff0c;它的分布式主要是借助hadoop实现&#xff0c;存储是hdfs&#xff0c;计算是mapr…

笔记本家庭版本win11上win+r,运行cmd默认没有管理员权限,如何调整为有管理员权限的

华为matebookeGo 笔记本之前有段时间不知怎么回事&#xff0c;打开运行框&#xff0c;没有了那一行“使用管理权限创建此任务”&#xff0c;而且cmd也不再是默认的管理员下的&#xff0c;这很不方便,虽然每次winr &#xff0c;输入cmd后可以按ctrlshitenter以管理员权限运行&am…

transformer位置编码最详细的解析

位置编码positional encoding 1. 位置编码是什么&#xff0c;为什么需要它&#xff1f;2. transformer提出的位置编码方法3. 直觉4. 其他细节5. 相对位置6. 常见问题解答 1. 位置编码是什么&#xff0c;为什么需要它&#xff1f; 位置和词语的顺序是任何语言的重要组成部分。它…

STM32f103入门(4)对射式红外传感器计次(外部中断)

中断:在主程序运行过程中&#xff0c;出现了特定的中断触发条件 (中断源)&#xff0c;使得CPU暂停当前正在运行的程序&#xff0c;转而去处理中断程序处理完成后又返回原来被暂停的位置继续运行中断优先级:当有多个中断源同时申请中断时&#xff0c;CPU会根据中断源的轻重缓急进…

useRef 定义的 ref 在控制台可以打印但是页面不生效?

useRef 是一个 React Hook&#xff0c;它能让你引用一个不需要渲染的值。 点击计时器 点击按钮后在控制台可以打印但是页面不生效。 useRef 返回的值在函数组件中不会自动触发重新渲染&#xff0c;所以控制台可以显示变化而按钮上无法显示 ref.current的变化。 import { use…

孙哥Spring源码第14集

第14集 BeanDefintion的创建方式 【视频来源于&#xff1a;B站up主孙帅suns Spring源码视频】 1、注册BeanDefintion的方式如何根据情况进行选择呢&#xff1f; xml 注解 扫描 import 2、为什么没有人用Import 可以通过注解的方式进行对应类型的注册。 3、ImportSelector和…

Lesson4-3:OpenCV图像特征提取与描述---SIFT/SURF算法

学习目标 理解 S I F T / S U R F SIFT/SURF SIFT/SURF算法的原理&#xff0c;能够使用 S I F T / S U R F SIFT/SURF SIFT/SURF进行关键点的检测 SIFT/SURF算法 1.1 SIFT原理 前面两节我们介绍了 H a r r i s Harris Harris和 S h i − T o m a s i Shi-Tomasi Shi−Tomasi…

Mac下使用Homebrew安装MySQL5.7

Mac下使用Homebrew安装MySQL5.7 1. 安装Homebrew & Oh-My-Zsh2. 查询软件信息3. 执行安装命令4. 开机启动5. 服务状态查询6. 初始化配置7. 登录测试7.1 终端登录7.2 客户端登录 参考 1. 安装Homebrew & Oh-My-Zsh mac下如何安装homebrew MacOS安装Homebrew与Oh-My-Zsh…

港陆证券:服装家纺公司上半年投资并购力度加大

9月1日&#xff0c;嘉曼服饰发布公告&#xff0c;为完善多品牌差异化开展战略&#xff0c;将以自有资金收买暇步士&#xff08;Hush Puppies&#xff09;品牌我国内地及香港、澳门区域IP财物。 面对服饰市场的激烈竞争&#xff0c;本年以来一些服饰类A股公司开启了“买买买”形…

SSRF服务端请求伪造

服务端请求伪造&#xff0c;其实就是攻击者构造恶意请求&#xff0c;服务端发起恶意请求&#xff0c;如果服务端不对用户传递的参数进行严格的过滤和限制&#xff0c;就可能导致服务端请求伪造 上面是百度识图&#xff0c;我们可以传递图片地址&#xff0c;百度识图向图片发起…

哈夫曼编码实现文件的压缩和解压

程序示例精选 哈夫曼编码实现文件的压缩和解压 如需安装运行环境或远程调试&#xff0c;见文章底部个人QQ名片&#xff0c;由专业技术人员远程协助&#xff01; 前言 这篇博客针对《哈夫曼编码实现文件的压缩和解压》编写代码&#xff0c;代码整洁&#xff0c;规则&#xff0…