当前位置: 首页 > news >正文

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

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

相关代码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);
}

相关文章:

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

相关代码gitee自取&#xff1a; C语言学习日记: 加油努力 (gitee.com) 接上期&#xff1a; 学C的第三十四天【程序环境和预处理】_高高的胖子的博客-CSDN博客 1 . 算法效率 &#xff08;1&#xff09;. 什么是数据结构&#xff1a; 数据结构(Data Structure)是计算机存储、…...

Jmete+Grafana+Prometheus+Influxdb+Nginx+Docker架构搭建压测体系/监控体系/实时压测数据展示平台+遇到问题总结

背景 需要大批量压测时&#xff0c;单机发出的压力能力有限&#xff0c;需要多台jmeter来同时进行压测&#xff1b;发压机资源不够&#xff0c;被压测系统没到瓶颈之前&#xff0c;发压机难免先发生资源不足的情形&#xff1b;反复压测时候也需要在不同机器中启动压测脚本&…...

php提交表单将html相互字符转化的封装函数

在 PHP 中&#xff0c;您可以使用 htmlspecialchars() 函数将 HTML 字符转换为文本。该函数将把 <、>、" 和 等特殊字符转换为对应的 HTML 实体&#xff0c;从而避免跨站点脚本&#xff08;XSS&#xff09;攻击。 例如&#xff0c;如果您有一个表单输入字段的值&a…...

7 Series FPGAs GTX/GTH Transceivers

目录 1. Overview2. Block Diagram3. Transmitter4. Receiver5. Physical Coding Sublayer(PCS)6. Physical Medium Attachment(PMA)本博客为Xilinx 7系列FPGA的千兆比特高速收发器(Gigabit Transceiver, GT)介绍 ug476 - 7 Series FPGAs GTX GTH TransceiversUser Guide…...

iOS系统下轻松构建自动化数据收集流程

在当今信息爆炸的时代&#xff0c;我们经常需要从各种渠道获取大量的数据。然而&#xff0c;手动收集这些数据不仅耗费时间和精力&#xff0c;还容易出错。幸运的是&#xff0c;在现代科技发展中有两个强大工具可以帮助我们解决这一问题——Python编程语言和iOS设备上预装的Sho…...

Android基础之Activity生命周期

Activity是Android四大组件之一、称为之首也恰如其分。 Activity直接翻译为中文叫活动。在Android系统中Activity就是我看到的一个完整的界面。 界面中看到的TextView(文字&#xff09;、Button(按钮)、ImageView&#xff08;图片&#xff09;都是需要Activity来承载的。 总…...

Golang 程序漏洞检测利器 govulncheck(一):安装和使用方法

govulncheck 是什么&#xff1f; govulncheck 是一个命令行工具&#xff0c;可以帮助 Golang 开发者快速找到项目代码和依赖的模块中的安全漏洞。该工具可以分析源代码和二进制文件&#xff0c;识别代码中对这些漏洞的任何直接或间接调用。 默认情况下&#xff0c;govulnchec…...

强化学习算法总结 2

强化学习算法总结 2 4.动态规划 待解决问题分解成若干个子问题&#xff0c;先求解子问题&#xff0c;然后得到目标问题的解 需要知道整个状态转移函数和价值函数&#xff0c;状态空间离散且有限 策略迭代&#xff1a; 策略评估:贝尔曼期望方程来得到一个策略的 V ( s ) V(s…...

修改node_modules避免更新覆盖 patch-package

说明&#xff1a;直接修改第三方库的代码&#xff0c;会带来团队协作的问题&#xff0c;使用patch-package生成补丁包 什么是 patch-package&#xff1f; patch-package 是一个基于 Git 的工具&#xff0c;它可以帮助我们对依赖包进行修复补丁。通过创建一个与问题相关的补丁文…...

Elasticsearch安装,Springboot整合Elasticsearch详细教程

Elasticsearch 是一个分布式、RESTful 风格的搜索和数据分析引擎&#xff0c;能够实现近乎实时的搜索。 Elasticsearch官网https://www.elastic.co/cn/ 这篇文章主要简单介绍一下Elasticsearch&#xff0c;Elasticsearch的java API博主也在学习中&#xff0c;文章会持续更新~ …...

OJ题库:计算日期到天数转换、打印从1到最大的n位数 、尼科彻斯定理

前言&#xff1a;在部分大厂笔试时经常会使用OJ题目&#xff0c;这里对《华为机试》和《剑指offer》中的部分题目进行思路分析和讲解&#xff0c;希望对各位读者有所帮助。 题目来自牛客网&#xff0c;欢迎各位积极挑战&#xff1a; HJ73:计算日期到天数转换_牛客网 JZ17:打印…...

混合动力汽车耐久测试

一 背景 整车厂可通过发动机和电机驱动的结合为多款车型提供混合动力驱动技术。汽车集成电机驱动可大大减少二氧化碳的排放&#xff0c;不仅如此&#xff0c;全电动驱动或混合动力驱动的汽车还将使用户体验到更好的驾驶感受&#xff0c;且这种汽车可通过电动机来实现更快的加速…...

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

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

【Java 动态数据统计图】动态数据统计思路案例(动态,排序,动态数组(重点推荐))七(129)

需求&#xff1a;前端根据后端的返回数据&#xff1a;画统计图&#xff1b; 说明&#xff1a; 1.X轴为地域&#xff0c;Y轴为地域出现的次数&#xff1b; 2. 动态展示&#xff08;有地域展示&#xff0c;没有不展示&#xff0c;且高低排序&#xff09; Demo案例&#xff1a; …...

Cell Reports | 揭开METTL14在介导m6A修饰中的神秘面纱

m6A被认为是最丰富的mRNA修饰&#xff0c;广泛分布在大多数真核生物中&#xff0c;包括哺乳动物、植物、昆虫、酵母和某些病毒。m6A修饰的沉积和去除之间的动态平衡对于正常的生物过程和发育至关重要&#xff0c;如失调通常与癌症等疾病有关。m6A修饰由m6A甲基转移酶复合物&…...

297. 二叉树的序列化与反序列化

题目描述 序列化是将一个数据结构或者对象转换为连续的比特位的操作&#xff0c;进而可以将转换后的数据存储在一个文件或者内存中&#xff0c;同时也可以通过网络传输到另一个计算机环境&#xff0c;采取相反方式重构得到原数据。 请设计一个算法来实现二叉树的序列化与反序…...

肖sir__设计测试用例方法之边界值03_(黑盒测试)

设计测试用例方法之边界值 边界点定义 上点&#xff1a;边界上的点 离点&#xff1a;离上点最近的点&#xff08;即上点左右两边最邻近的点&#xff09; 内点&#xff1a;在域范围内的点 案例&#xff1a;qq号&#xff1a;5-12位 闭区间&#xff1a; 离点&#xff1a;5 位 &…...

功能测试常用的测试用例大全

登录、添加、删除、查询模块是我们经常遇到的&#xff0c;这些模块的测试点该如何考虑 1)登录 ① 用户名和密码都符合要求(格式上的要求) ② 用户名和密码都不符合要求(格式上的要求) ③ 用户名符合要求&#xff0c;密码不符合要求(格式上的要求) ④ 密码符合要求&#xff0c;…...

css利用flex分配剩余高度出现子组件溢出问题

1.利用flex分配剩余高度/宽度 情景&#xff1a;父组件高度一定&#xff0c;子组件中&#xff0c;其他子组件高度固定&#xff0c;一个子组件高度不确定&#xff08;页面滚动列表&#xff09; .father{display: flex;flex-direction: column;.son1{height: 200px;}.son2{//或 …...

Java中的网络编程------基于Socket的TCP编程和基于UDP的网络编程,netstat指令

Socket 在Java中&#xff0c;Socket是一种用于网络通信的编程接口&#xff0c;它允许不同计算机之间的程序进行数据交换和通信。Socket使得网络应用程序能够通过TCP或UDP协议在不同主机之间建立连接、发送数据和接收数据。以下是Socket的基本介绍&#xff1a; Socket类型&…...

Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)

文章目录 1.什么是Redis&#xff1f;2.为什么要使用redis作为mysql的缓存&#xff1f;3.什么是缓存雪崩、缓存穿透、缓存击穿&#xff1f;3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...

Module Federation 和 Native Federation 的比较

前言 Module Federation 是 Webpack 5 引入的微前端架构方案&#xff0c;允许不同独立构建的应用在运行时动态共享模块。 Native Federation 是 Angular 官方基于 Module Federation 理念实现的专为 Angular 优化的微前端方案。 概念解析 Module Federation (模块联邦) Modul…...

C++中string流知识详解和示例

一、概览与类体系 C 提供三种基于内存字符串的流&#xff0c;定义在 <sstream> 中&#xff1a; std::istringstream&#xff1a;输入流&#xff0c;从已有字符串中读取并解析。std::ostringstream&#xff1a;输出流&#xff0c;向内部缓冲区写入内容&#xff0c;最终取…...

【C语言练习】080. 使用C语言实现简单的数据库操作

080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...

C++使用 new 来创建动态数组

问题&#xff1a; 不能使用变量定义数组大小 原因&#xff1a; 这是因为数组在内存中是连续存储的&#xff0c;编译器需要在编译阶段就确定数组的大小&#xff0c;以便正确地分配内存空间。如果允许使用变量来定义数组的大小&#xff0c;那么编译器就无法在编译时确定数组的大…...

CVE-2020-17519源码分析与漏洞复现(Flink 任意文件读取)

漏洞概览 漏洞名称&#xff1a;Apache Flink REST API 任意文件读取漏洞CVE编号&#xff1a;CVE-2020-17519CVSS评分&#xff1a;7.5影响版本&#xff1a;Apache Flink 1.11.0、1.11.1、1.11.2修复版本&#xff1a;≥ 1.11.3 或 ≥ 1.12.0漏洞类型&#xff1a;路径遍历&#x…...

【Linux】Linux 系统默认的目录及作用说明

博主介绍&#xff1a;✌全网粉丝23W&#xff0c;CSDN博客专家、Java领域优质创作者&#xff0c;掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围&#xff1a;SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...

并发编程 - go版

1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程&#xff0c;系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...

Unity UGUI Button事件流程

场景结构 测试代码 public class TestBtn : MonoBehaviour {void Start(){var btn GetComponent<Button>();btn.onClick.AddListener(OnClick);}private void OnClick(){Debug.Log("666");}}当添加事件时 // 实例化一个ButtonClickedEvent的事件 [Formerl…...

pikachu靶场通关笔记19 SQL注入02-字符型注入(GET)

目录 一、SQL注入 二、字符型SQL注入 三、字符型注入与数字型注入 四、源码分析 五、渗透实战 1、渗透准备 2、SQL注入探测 &#xff08;1&#xff09;输入单引号 &#xff08;2&#xff09;万能注入语句 3、获取回显列orderby 4、获取数据库名database 5、获取表名…...