【数据结构】常见时间复杂度以及空间复杂度

【数据结构】常见时间复杂度以及空间复杂度

时间复杂度与空间复杂度

一、复杂度的概念

  • 一个算法的好坏,主要是对比两者的时间和空间两个维度,也就是时间和空间复杂度。
  • 时间复杂度主要衡量一个算法运行的快慢,空间复杂度主要衡量一个算法运行需要的额外空间

二、时间复杂度

  • 算法的时间复杂度是一个函数式T(N),算法中的基本操作的执行次数,为算法的时间复杂度。
  • 注:编译器的不同,编译所需要的时间也不同。越新的编译器,编译的时间往往比旧的编译器快
  • 当一个算法函数式为T(N) = N,和另一个算法函数式为 T(N) = N^2比较,必然是第一个快

1、大O的渐进表示法

大的渐进表示法的规则:

时间复杂度函数式T(N)中,只保留最高阶项,去掉那些低阶项(当N无穷大时,低阶项的影响越来越小)如果最高阶项是一个一次线性函数,则去除常数系数(当N无穷大时,1的影响很小)T(N)中如果没有N相关的项目,只有常数项,用常数1取代所有加法常数

我们来判断一段代码的时间复杂度

// 请计算⼀下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) = N2 + 2 ∗ N + 10
通过对N取值分析,对结果影响最大的⼀项是N2
通过以上方法,可以大致评估Func1的时间复杂度为:O(N2 )

2、函数clock计算运算时间

我们想计算代码运算的时间,可以运用clock函数进行计算。运算过程为运算末-运算初

#include<stdio.h> #include<time> int main() { int i = 0; int begin = clock(); int x = 10; for(i = 0; i < n; i++) { x++; } int end = clock(); //计算运行时间 printf("%dms", end - begin); return 0; } 

3、常见复杂度对比

52013140(1)常数阶
3n+4O(n)线性阶
3n^2+4n+50(n^2)平方阶
310g(2)n+40(1ogn)对数阶
2n+3nlog(2)n+14O(nlogn)nlogn阶
n3+2n2+4n+60(n^3)立方阶
2^n0(2^n)指数阶

3.1常数项复杂度

#include<stdio.h> int main() { int x = 0; scnaf("%d", &x); printf("%d", x); return 0; } 

执行的基本操作次数:T (N) = 3
根据推导规则第3条得出时间复杂度为:O(1)

3.2线性时间复杂度

案例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执行的基本操作次数:T (N) = 2N + 10
根据推导规则第3条得出Func2的时间复杂度为:O(N)

案例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执行的基本操作次数:T (N) = M + N
因此:Func3的时间复杂度为:O(N)

3.3平方阶复杂度

#include<stdio.h> int main() { int x = 0; int begin = clock(); int n = 100000; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { x++; } } int end = clock(); printf("%d\n", x); printf("%dms\n", end - begin); return 0; } 

执行的基本操作次数:T (N) = i * j
因此:时间复杂度为:O(N^2)

3.4对数复杂度

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

当n=2时,执行次数为1
当n=4时,执行次数为2
当n=16时,执行次数为4
假设执行次数为x ,则2x=n
因此执行次数:x=log n
因此:func5的时间复杂度取最差情况为:O(log2 n)

3.5递归函数

单递归

递归时间复杂度:所有递归调用次数的累加

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

调⽤⼀次Fac函数的时间复杂度为 O(1),而在Fac函数中,存在n次递归调用Fac函数
因此:return 1;
阶乘递归的时间复杂度为:O(n)

![[Pasted image 20251107091622.png]]

我们再来看一下往递归里加个for循环:此时递归的时间复杂度为:O(n^2)

![[bit-2025-11-07-09-21-07.png]]
双递归
![[bit-2025-11-07-09-44-15.png]]

三、空间复杂度

空间复杂度算的是变量个数,是对一个算法在运行过程中临时占用存储空间大小的量度,同样也使用大O渐进表示法。(一般在编程中不考虑空间复杂度,而多用时间复杂度。空间复杂度多运用在嵌入式)

注意:函数运行时所需要的栈空间(存储参数、局部变量、一些寄存器信息等)在编译期间已经确定好了,因此空间复杂度主要通过函数在运行时候显式申请的额外空间来确定
我们先来看一下经典的冒泡排序

冒泡排序O(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; } } 

函数栈帧在编译期间已经确定好了,只需要关注函数在运行时额外申请的空间。
BubbleSort额外申请的空间有exchange等有限个局部变量,使用了常数个额外空间,因此空间复杂度为 O(1)

三个反置O(N)

void reverse(int* nums, int left, int right) { while (left < right) { int tap = nums[left]; nums[left] = nums[right]; nums[right] = tap; left++; right--; } } int main() { int nums[] = { 1,2,3,4,5,6,7 }; int numsSize = sizeof(nums) / sizeof(nums[0]); int k = 0; scanf("%d", &k); k %= numsSize; reverse(nums, 0, numsSize - k - 1); reverse(nums, numsSize - k, numsSize - 1); reverse(nums, 0, numsSize - 1); for (int i = 0; i < numsSize; i++) { printf("%d ", nums[i]); } return 0; } 
  • 由于创建了个数组,数组的空间复杂度为O(N)
    空间复杂度一般只会出现O(1),O(N),O(N^2),在复杂度中,还是更看重时间复杂度

Read more

【python】第六节anacoda+配置Jupyter notebook

先下载安装好anacoda →Advance AI with Open Source | Anaconda 打开安装好的anaconda 通过cmd打开 可以在桌面创建快捷方式 为什么分析数据大家要使用jupyter book呢 有的时候一个py文件数据量太大,并非想要全部都要让它运行,那会浪费太多的时间了,所以使用Jupyter book 1.交互模式,交互模式之下不用print打印语句,我们就可以看到结果 2.html格式直接分享,可以看到你的思考模式 3.可以用Latex插入公式 安装Jupyter book 打开终端,windows 在开始的地方输入cmd 输入pip install notebook 等待安装结束之后,输入jupyter book查看是否安装完毕 浏览器弹出一个notebook窗口,说明安装成功啦 终止jupyter notebook 不光要叉掉浏览器,还要在终端的地方按住control c,在他询问你是否关闭的时候输入 y 后台服务器就会终止了 jupyter notebook的使用 打开cmd

By Ne0inhk
【数据结构初阶】--快速排序进阶

【数据结构初阶】--快速排序进阶

🔥个人主页:@草莓熊Lotso 🎬作者简介:C++研发方向学习者 📖个人专栏: 《C语言》 《数据结构与算法》《C语言刷题集》《Leetcode刷题指南》 ⭐️人生格言:生活是默默的坚持,毅力是永久的享受。 前言: 在之前的博客中我们实现了递归版本和非递归版本的快速排序,其中递归版本中的找基准的方法我们学习了三种。但是有些特殊的情况,比如重复元素过多或者已经有序的时候,我们的时间效率就会受到影响了,这次的进阶篇中,我们会通过一些方法来优化快速排序 目录 一.三数取中和随机数选择基准 三数取中法: 随机数选择法:  两种方法的对比分析 :  二.三路划分 实现步骤:  代码实现:  三路划分和传统二路划分思路的对比:   三.自省排序 核心思想:  代码实现: 一.三数取中和随机数选择基准 三数取中法: 原理:从子数组的首元素、尾元素、中间元素中选择中位数作为基准。通过选取中间大小的值,避免极端值(如最大/最小值)作为基准,从而平衡左右子数组的划分。 核心逻辑:

By Ne0inhk

从零到一:BLDC/PMSM恒功率算法在智能家居风扇中的实战应用

智能家居风扇的BLDC/PMSM恒功率控制:从算法设计到用户体验优化 1. 引言:智能家居风扇的特殊需求 在炎热的夏季,一台安静、节能且能自动调节风速的智能风扇已经成为现代家庭的标配。传统交流电机风扇的嗡嗡声和忽高忽低的转速早已无法满足追求品质生活的用户需求。BLDC(无刷直流)和PMSM(永磁同步)电机凭借其高效率、低噪音和精准控制特性,正在重塑智能家居风扇市场。 与工业场景不同,家用风扇对电机控制提出了独特挑战: * 静音优先:卧室环境下,30分贝以下的运行噪音是基本要求 * 能效敏感:全年不间断使用使得每瓦功耗都影响电费账单 * 交互友好:需要响应手机APP、语音控制等多模式指令 * 安全可靠:长时间运行必须杜绝过热风险 恒功率算法在这其中扮演着关键角色——它不仅是保护电机和电子元件的安全阀,更是实现"智能"的基础。当用户设定"睡眠模式"时,算法需要动态平衡风量、噪音和功耗;当检测到电压波动时,又要确保转速稳定不突变。这些需求催生了专为智能家居优化的BLDC/PMSM控制方案。 2. 恒功率控制的核心原理 2.

By Ne0inhk
12306反反爬虫策略:Python网络请求优化实战

12306反反爬虫策略:Python网络请求优化实战

一、引言:12306反爬虫的严峻挑战 12306作为中国铁路售票系统,每天面临着海量的抢票请求,其反爬虫机制异常严格:IP封锁、验证码、请求频率限制、会话追踪等。要在这样的环境下实现稳定抢票,必须设计一套完善的反反爬虫策略。12306抢票项目通过CDN加速、代理IP、请求频率控制和"小黑屋"机制等技术,成功突破了12306的反爬虫防线。 二、CDN加速:突破网络瓶颈 1. 实现原理 CDN(内容分发网络)通过将资源分发到全球各地的节点,使用户可以就近获取所需内容,提高访问速度。12306项目通过筛选和使用高速CDN节点,加速与12306服务器的通信。 2. 代码实现 核心文件:d:\python-code\12306-master\init\select_ticket_info.py defcdn_certification(self):"""CDN认证与筛选&

By Ne0inhk