数据结构——复杂度讲解

已经太久没用更新了,由于各种原因,导致很久没用更新了,但是停更期间我也是一直在很努力的学习与复习之前学过的知识,读了两本C语言的数据,初学者也是可以看的,推荐给大家,如果需要pdf,可以私信我

第一本:《C陷阱与缺陷》

第二本:《C语言深度剖析》

回归正题,今天开始正式学习数据结构,我将带领大家由浅入深的学习,不用害怕,因为我会把我认为比较难懂的知识学习好几遍,然后再给大家写出来。今天所讲的算法复杂度难度还好,不是很难,大家放心,不用担心学不会。

正式开始学习:

目录

1、算法复杂度

2、如何学好数据结构

3、算法效率

3.1、什么是算法效率?

3.2、复杂度的计算

4、时间复杂度

4.1、大O的渐进表示法

4.2时间复杂度计算示例

4.2.1、示例1:

4.2.2、示例2:

4.2.3、示例3:

4.2.4、示例4:

4.2.5、示例5:

4.2.6、示例6:

4.2.7、示例7:

5、空间复杂度

5.1 空间复杂度计算⽰例

5.1.1 ⽰例1

5.1.2 ⽰例2

6、常⻅复杂度对⽐

7、复杂度算法题

7.1 旋转数组

思路1

思路2

思路3


1、算法复杂度

数据结构与算法介绍:

数据结构:数据结构的内存中存储数据的格式,指的是相互之间存在一种或多种特定关系的数据元素集合与数据元素之间的集合,一种数据结构不会适用于全部的用途,所以我们要学习数据结构,数据结构包括:数组、链表、线性表、哈希值等等。

算法:算法是指数据输入与输出时,在底层中是如何实现,有的算法对于程序运行快,有的慢,但实现的功能都是一样的。

总结:数据结构管理在内存中是如何放置的,算法是如何在内存中计算的

数据结构与算法的概念是不是特别的简单,所以大家不要听到数据结构这门课的名字就害怕自己学不会,我们把这些拗口的语言给打回原形,让它们看起来也没用这么高大尚。

2、如何学好数据结构

有小伙伴问了,我们应该如何学习数据结构呢,我这里给出两点的建议:

秘诀1:死磕代码!!!

秘诀2:画图画图画图+思考!!!

3、算法效率

3.1、什么是算法效率?

算法效率就是我们所写的代码运行的速度快慢,比如:有两个人一起学习,学习的时间地点都是一样的,第一个人学的很快,第二个人学的很慢,很明显,第一个人学习效率高,第二个人学习效率低,这就是因为学习方法不一样导致的,虽然这两个人都可以学会相同的知识。

我们所写的代码也是如此,两个人写同一道算法题目时候,一个人写的代码运行效率快,一个人写的代码运行效率慢,但是最终实现的结构都是相同的。

如何衡量⼀个算法的好坏呢?

案例:旋转数组https://leetcode.cn/problems/rotate-array/description/

思路:循环K次将数组所有元素向后移动⼀位

void rotate(int* nums, int numsSize, int k) {while (k--) {int end = nums[numsSize - 1];for (int i = numsSize - 1; i > 0; i--) {nums[i] = nums[i - 1];}nums[0] = end;}
}

在我们刚学完C语言的同学们,肯定第一时间想到的是使用两个for循环进行嵌套到达交换,我也是第一时间想到的是两个for循环进行嵌套,那么这个写法到底对不对呢?有没有通过测试?

我们看见了,这个题目一共会验证38个输入数据,我们通过了0-36个输入数据,在进行第37个输入数据时候,显示超出计算时间,显然,我们算法实现的功能是没用问题的,主要的问题在于在进行很大数计算的时候,我们所写的代码运行时间超出了题目给定的时间限制,这时候我们就需要优化我们的算法了。

3.2、复杂度的计算

一般我们衡量一个算法的好坏是从时间与空间两个维度进行衡量的,分别成为时间复杂度与空间复杂度

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

空间复杂度是衡量是指一个算法在运行时所需要的额外空间。

4、时间复杂度

定义:在计算机科学中,算法的时间复杂度是一个函数T(N),他定量描述了算法的运行时间,时间复杂度是该程序运行的时间快慢,那么我们为什么不直接计算程序的运行时间呢?

  1. 在相同的机器上,相同的代码分别放入新编译器与老编译器中,程序所执行的运行时间快慢是不一样的。
  2. 不同配置的电脑,在同一款编译器运行相同的代码,由于电脑的配置不一样,会导致计算出来的程序运行的速度快慢不一样
  3. 测试代码运行的时候,需要在代码完成以后才能进行测试,会耽误我们写代码。

总结:所以我们通过一个T(N)的算法模型,就可以大致判断代码的运行效率,可以大大提高我们代码的算法与书写效率。

案例:

前情提要:在我们计算T(N)的时候,我们所需要计算的是程序中最坏的情况(最大运行的时间)

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

我们如何计算T(N)呢?

我们需要找到程序中导致程序运行速度的主要地方,但是我们这一次分析程序全部的运行次数,让大家方便理解。

  1. 在一个for循环中又嵌套一个for循环,而且这两个循环的判断语句都是i,j<N,所以我们最坏的运行情况是外循环执行N次,内循环执行N次。N*N
  2. 在第二个循环中,判断语句是2N,这是很好看出来的。2N
  3. 第三个是M=10,占用十次运算。10

得出来我们的T(N):N^2+2N+10.

如果N=10;

10^2+20+10=130

如果N=100;

100^2+200+10=10210

如果N=100000。

100000^2+200000+10= 10000200010

通过上述三个给N的数值,我们可以看出来,N^2对该代码的运行效率所产生的运行时间快慢影响是最大的,从而我们可以写出来该程序的时间复杂度是T(N^2)。

我们无需精确的计算程序每一条语句的计算时间,这是无意义的,因为对于计算机而言,执行的速度是很快的,我们人类认为很大的数字,计算机一下子就可以计算完毕。

上面我们看见当N不断变大时常数和低阶项对结果影响很小,所以我们只需要计算程序能代表最大增长量级的大概执行次数,复杂度的表示通常使用大O的渐进法来表示。

4.1、大O的渐进表示法

函数的渐近行为是描述算法的时间复杂度与空间复杂度随着输入规模增加时的变化趋势,他帮助我们理解在处理非常大的数据时,算法的性能表现

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

  1. 在时间复杂度T(N)中,只保留最高项,忽略低阶项,因为随着输入规模越来越大时,低阶项的影响越来越小
  2. 如果最高项存在且不是1,如:2N,那么就忽略常数项,直接写N。因为当N不断变大的时候,常数项的影响会越来越小。还用2N这个例子:当N位无穷大的时候,如果2无穷大也等于无穷大,所以我可以直接忽略
  3. 如果T(N)中没用N,是一个常数的话,那么我们就直接写成O(1),因为对于计算机来说,计算机运行的速度是非常快的,我们人类觉得大的数字,计算机很快就能运行完。

介绍完大O以后,大家还是不理解,那么我就先说出我学习这部分的疑惑,给大家解释一下:

1、N是什么?N是我们输入的数据,可以很大可以很小,比如我们第一个代码例子中,输入数据小的时候,可以通过测试,输入数据过大的时候,就会超时运行,所以N看作是一个变量

2、如果是O(10000000000000)那还要写成O(1)吗?是的,是需要写成O(1),还是那句话,对于我们人来说”10000000000000“这个数很大,但是对于机器来说,运行速度很快就可以完成运行。如果这里大家实在不理解,后续我会给出一张运行效率的图标,大家可以先试着背一下,后续再慢慢理解也是可以的,如果实在不理解,可以私信问我。

4.2时间复杂度计算示例

4.2.1、示例1:

// 计算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函数中的时间复杂度是2*N+10,在上述所说的大O符号表示法中,在2N中常数项2对代码运行效率影响是比较低的,同时常数10对代码影响也是较低,所以取对代码运行效率最大的,也就是我们刚刚所说的大O。

所以Func2函数中时间复杂度可以使用O(N)来表示。

4.2.2、示例2:

// 计算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函数中,我们的输入变量一共有两个,在Func3中有三种大O的表示方法:

1、N>> M那么写成O(N)

2、M>>N那么写成O(N)

3、N≈M那么写成O(N+M)

注意:这不是循环嵌套,如果是循环嵌套那么写成O(N*M)。

4.2.3、示例3:

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

在Func4函数中,很明显的一点就是,判断for循环的条件是K<100,Func4函数传进来的形参N并不会在运行这个函数中用到,在这个函数中时间复杂度是O(100),在上面所讲的大O的表示形式中第三条可以得到,Func3函数的时间复杂度为O(1)。

4.2.4、示例4:

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

strchr函数是为了实现输入一个字符(character),在字符串(str)中查找我们所输入的字符是否在str中,如果存在那么就return p_begin;,否则就return NULL;

在这个函数中查找字符时候有三种时间复杂度T(N).

1、如果我们所需要查找的字符在字符串开头就找到了,那么时间复杂度表示T(N)=1;

2、如果我们所需要查找的字符在字符串中间找到了,那么时间复杂度表示T(N)=2/N;

3、如果我们所需要查找的字符在字符串末尾才找到了,那么时间复杂度表示T(N)=N;

我们可以得到大O分别为:O(1),O(N),O(N)

我们找出了这三种字符在字符串中查找出现的位置,我们上述所说,我们函数渐进式影响运行效率最大的数据,那么我们就可以很明显的得到为O(N).

4.2.5、示例5:

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

上述代码是冒泡排序,当外层循环一次,内存循环实现数据交换,这个函数并不是像两个一样由一个判断语句控制,所以分析这个代码的时候,要小心点。

每经历一次循环,end就会变小,交换的次数就会变少,所以这是一个等差数列,通过等差数列的公式我们可以得到,时间复杂度为O(N^2)。

4.2.6、示例6:

// 计算Func4的时间复杂度?
void func5(int n)
{int cnt = 1;while (cnt < n){cnt *= 2;}
}

Func5函数中,循环的判断语句是cnt<n。

我们假设n=10,我们循环的时候,第三次循环cnt=8还是cnt<n,当第四次循环cnt直接等于16了,此时cnt>n停止循环。

此时我们如何写时间复杂度呢?

可以使用log来表示   logn^16=4,此时系统就可以知道,我们执行了四次循环,时间复杂度我们就可以直接描述为O(logn),底数写不写都一样,计算机执行速度很快,对于速度影响不大。

4.2.7、示例7:

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

递归的时间复杂度是由他递归调用的次数和每次调用计算开销来决定的,我们一般分析最坏情况下的递归深度和工作量

在这个函数中调用fac的时间复杂度是O(1),但是fac函数存在n次调用的情况,所以是O(N)。

5、空间复杂度

空间复杂度是在该函数中,除去计算机先前已经开辟好的空间,在该函数中额外需要再次开辟的空间。

5.1 空间复杂度计算⽰例

5.1.1 ⽰例1

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

最为简单的理解计算,看系统在这段函数中开辟了几个栈帧,这个函数中开辟的栈帧为3个

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

5.1.2 ⽰例2

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

在每次进行递归的时候,都会创建新的栈帧(额外的空间)

空间复杂度为:O(N)

6、常⻅复杂度对⽐

曲线越平缓性能越好,复杂度越小。

7、复杂度算法题

7.1 旋转数组

回到我们刚开始讲解的旋转数组题目,我们大致明白要怎么样减小时间复杂度了,如果能让我们之前所写的第一个代码的时间缩小一级,那会不会通过呢?

思路1

时间复杂度  O(n2)
循环K次将数组所有元素向后移动⼀位(代码不通过)
void rotate(int* nums, int numsSize, int k) {while (k--){int end = nums[numsSize - 1];for (int i = numsSize - 1; i > 0; i--){nums[i] = nums[i - 1];} nums[0] = end;}
}

思路2

空间复杂度  O(n)

申请新数组空间,先将后k个数据放到新数组中,再将剩下的数据挪到新数组中。

void rotate(int* nums, int numsSize, int k)
{int newArr[numsSize];for (int i = 0; i < numsSize; ++i){newArr[(i + k) % numsSize] = nums[i];} for (int i = 0; i < numsSize; ++i){nums[i] = newArr[i];}
}

思路3

空间复杂度  O(1)  
• 前n - k个逆置:4 3 2 1 5 6 7
• 后k个逆置    :4 3 2 1 7 6 5
• 整体逆置     :5 6 7 1 2 3 4
void reverse(int* nums, int begin, int end)
{while (begin < end) {int tmp = nums[begin];nums[begin] = nums[end];nums[end] = tmp;begin++;end--;}
} v
oid rotate(int* nums, int numsSize, int k)
{k = k % numsSize;reverse(nums, 0, numsSize - k - 1);reverse(nums, numsSize - k, numsSize - 1);reverse(nums, 0, numsSize - 1);
}

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

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

相关文章

【车载以太网】【SOME/IP】Wireshark 解析

目录 ​​​​​​​Wireshark 官方插件 相关代码: 启用协议插件 Lua插件 测试数据包 Wireshark 下载链接:Wireshark Go DeepSOMEIP插件介绍:https://www.wireshark.org/docs/dfref/s/someip.html官方插件 Wireshark从3.2版本开始支持SOME/IP,启用相应的插件即可以…

机器学习-梯度下降实验一

import numpy as np import pandas as pd import matplotlib.pyplot as plt from sklearn.model_selection import train_test_split, KFold from sklearn.metrics import mean_squared_error, r2_score from mpl_toolkits.mplot3d import Axes3D # 用于3D图plt.rcParams[fon…

【C++】——继承详解

目录 1、继承的概念与意义 2、继承的使用 2.1继承的定义及语法 2.2基类与派生类间的转换 2.3继承中的作用域 2.4派生类的默认成员函数 <1>构造函数 <2>拷贝构造函数 <3>赋值重载函数 <4析构函数 <5>总结 3、继承与友元 4、继承与静态变…

使用ESP8266和OLED屏幕实现一个小型电脑性能监控

前言 最近大扫除&#xff0c;发现自己还有几个ESP8266MCU和一个0.96寸的oled小屏幕。又想起最近一直想要买一个屏幕作为性能监控&#xff0c;随机开始自己diy。 硬件&#xff1a; ESP8266 MUColed小屏幕杜邦线可以传输数据的数据线 环境 Windows系统Qt6Arduino Arduino 库…

【蔡英丽医生】小细节大影响:解读血栓来临前的身体语言!

血栓&#xff0c;这一隐形的健康杀手&#xff0c;常常在不经意间悄然降临&#xff0c;给人们的健康带来严重威胁。了解血栓来临前的身体语言&#xff0c;对于及早预防和治疗至关重要。今天&#xff0c;我们特别邀请到北京中医药大学东方医院脑病科的副主任医师——蔡英丽医生&a…

极速上云2.0范式:一键智连阿里云

在传统上云的现状与挑战&#xff1a; 专线上云太重&#xff0c;VPN上云不稳&#xff0c;云上VPC&#xff0c;云下物理网络&#xff0c;多段最后一公里...... 层层对接&#xff0c;跳跳延迟&#xff0c;好生复杂! 当你试图理解SD-WAN供应商和阿里云的文档&#xff0c;以协调路由…

介绍一些免费 的 html 5模版网站 和配色 网站

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、H5 网站介绍网站 二、配色网站个人推荐 前言 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、H5 网站介绍 以下是一些提供免费…

构建响应式 Web 应用:Vue.js 基础指南

构建响应式 Web 应用&#xff1a;Vue.js 基础指南 一 . Vue 的介绍1.1 介绍1.2 好处1.3 特点 二 . Vue 的快速入门2.1 案例 1 : 快速搭建 Vue 的运行环境 , 在 div 视图中获取 Vue 中的数据2.2 案例 2 : 点击按钮执行 vue 中的函数输出 vue 中 data 的数据2.3 小结 三 . Vue 常…

PHP安全

PHP伪协议&#xff1a; 一.【file://协议】 PHP.ini&#xff1a; file:// 协议在双off的情况下也可以正常使用&#xff1b; allow_url_fopen &#xff1a;off/on allow_url_include&#xff1a;off/on file:// 用于访问本地文件系统&#xff0c;在CTF中通常用来读取本地文…

如何用安卓玩Java版Minecraft,安卓手机安装我的世界Java版游戏的教程

安卓手机使用FCL启动器安装我的世界Java版游戏的教程。如何用安卓玩Java版Minecraft 视频教程&#xff1a;https://www.bilibili.com/video/BV1CctYebEzR/ 前言 目前&#xff0c;安卓设备上可以用来运行Java版Minecraft的启动器主要有以下几款&#xff1a; PojavLauncher&a…

dedecms(四种webshell姿势)、aspcms webshell漏洞复现

一、aspcms webshell 1、登陆后台&#xff0c;在扩展功能的幻灯片设置模块&#xff0c;点击保存进行抓包查看 2、在slideTextStatus写入asp一句话木马 1%25><%25Eval(Request(chr(65)))%25><%25 密码是a&#xff0c;放行&#xff0c;修改成功 3、使用菜刀工具连…

第十一章 【后端】商品分类管理微服务(11.3)——商品管理模块 yumi-etms-goods

11.3 商品管理模块 yumi-etms-goods 新建 yumi-etms-goods 模块 添加依赖 pom.xml <?xml version="1.0" encoding="UTF-8"?> <project xmlns&#

【数字集成电路与系统设计】Chisel/Scala简介与Verilog介绍

目录 一、芯片前端设计开发背景知识 二、Verilog介绍 2.1 硬件设计一些重要概念 2.2 功能性仿真 2.3 简单的Verilog代码例子&#xff08;4-bit的加法器&#xff09; 三、Chisel简介 3.1 Chisel基本概念 3.2 Chisel代码展示 3.3 Chisel转成Verilog代码 四、Scala入…

Notepad++插件:TextFX 去除重复行

目录 一、下载插件 TextFX Characters 二、去重实操 2.1 选中需要去重的文本 2.2 操作插件 2.3 结果展示 2.3.1 点击 Sort lines case sensitive (at column) 2.3.2 点击 Sort lines case insensitive (at column) 一、下载插件 TextFX Characters 点【插件】-【插件管理…

数学学习记录

目录 学习资源&#xff1a; 9月14日 1.映射&#xff1a;​编辑 2.函数: 9月15日 3.反函数&#xff1a; 4.收敛数列的性质 5.反三角函数&#xff1a; 9月16日 6.函数的极限&#xff1a; 7.无穷小和无穷大 极限运算法则&#xff1a; 学习资源&#xff1a; 3Blue1…

【Elasticsearch系列九】控制台实战

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 推荐:kwan 的首页,持续学…

【matlab】生成 GIF 的函数(已封装可直接调用)

文章目录 前言一、函数输入与输出二、函数代码三、例程&#xff08;可直接运行&#xff09;参考文献 前言 生成 gif 图片时遇到的问题&#xff0c;为了后续调用方便&#xff0c;封装为函数 一、函数输入与输出 输入&#xff1a; cell_figure: cell 数组&#xff0c;数组元素是…

热成像目标检测数据集

热成像目标检测数据集 V2 版本 项目背景 热成像技术因其在安防监控、夜间巡逻、消防救援等领域的独特优势而受到重视。本数据集旨在提供高质量的热成像图像及其对应的可见光图像&#xff0c;支持热成像目标检测的研究与应用。 数据集概述 名称&#xff1a;热成像目标检测数据…

CSS框架 Tailwind CSS

文章目录 前言一、Tailwind CSS是什么&#xff1f;二、项目中如何使用1.安装Tailwind CSS2.初始化Tailwind CSS该处使用的url网络请求的数据。3.引入Tailwind CSS样式4.进行配置&#xff08;tailwind.config.js&#xff09;5.全局引入注册6.使用Tailwind CSS 总结 前言 Tailwi…

IP-adapter masking

https://github.com/huggingface/diffusers/issues/6802https://github.com/huggingface/diffusers/issues/6802