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

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

时间复杂度与空间复杂度

一、复杂度的概念

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

二、时间复杂度

  • 算法的时间复杂度是一个函数式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

LLaMA-Factory 大模型微调平台

LLaMA-Factory 大模型微调平台

目录 文章目录 * 目录 * LLaMA-Factory * LLaMA-Factory + Qwen3-7B + LoRA * 安装部署 * 准备数据集 * 执行微调 * 批量推理和训练效果评估 * LoRA 模型合并导出 * 部署运行微调后的大模型 LLaMA-Factory Llama-Factory 是基于 transformers 库开发的训练、微调、推理一体化平台,支持预训练、指令监督微调、奖励模型训练、PPO 训练、DPO 训练、KTO 训练、ORPO 训练等多种训练范式。支持使用 Accelerate 或 DeepSpeed 作为训练加速后端。 使用 Llama-Factory 进行微调非常简单,因为其最大的优势在于强大的数据处理与训练配置能力。只要按照官方的文档配置好环境,直接运行对应的脚本即可。 LLaMA-Factory + Qwen3-7B + LoRA 安装部署 * 容器安装 git clone

By Ne0inhk

Qwen3-1.7B代码生成效果如何?GitHub Copilot类比评测

Qwen3-1.7B代码生成效果如何?GitHub Copilot类比评测 最近,阿里开源了新一代的千问大模型系列——Qwen3。这个系列阵容强大,从0.6B到235B,各种尺寸都有。今天,咱们不聊那些动辄几百亿参数的大块头,就聚焦一个特别有意思的小家伙:Qwen3-1.7B。 为什么是它?因为1.7B这个参数量,刚好卡在一个很微妙的位置:它比那些动辄几十亿参数的“大模型”轻巧得多,理论上部署和推理成本都更低;但又比一些纯玩具级别的微型模型要“聪明”不少。更重要的是,它主打的就是代码生成能力。 这让我立刻想到了一个“参照物”——GitHub Copilot。作为目前最流行的AI编程助手,Copilot几乎成了代码生成的代名词。那么,这个新来的、开源的、只有1.7B参数的Qwen3,在代码生成这件事上,到底有几斤几两?它能达到Copilot几成的功力?还是说,它有自己的独特优势? 这篇文章,我就带你一起上手实测,用最直观的方式,看看Qwen3-1.7B在代码生成上的真实表现,

By Ne0inhk

PyCharm+GitHub Copilot零成本配置手册:学生认证/2FA/汉化疑难一次解决

PyCharm + GitHub Copilot 零成本配置手册:从学生认证到流畅编码的全链路实战 作为一名学生开发者,你是否曾羡慕那些能流畅使用AI编程助手的同行,却苦于复杂的认证流程、网络环境的掣肘,或是面对英文界面时的些许不适?将前沿的AI工具无缝融入日常开发工作流,本应是一个提升效率的愉悦过程,而非充满障碍的挑战。今天,我们就来彻底解决这些问题,打造一套专为学生群体设计、开箱即用的PyCharm与GitHub Copilot生产力解决方案。这套方案不仅会手把手带你完成从学生身份验证到IDE集成的每一步,更会聚焦于国内用户常见的“水土不服”问题,提供稳定的替代方案和优化技巧,让你真正零成本、零门槛地拥抱AI辅助编程。 1. 基石构建:GitHub学生认证与账户安全加固 在享受任何福利之前,一个经过验证且安全的GitHub账户是首要前提。学生认证是获取GitHub Copilot Pro免费使用权的钥匙,而双重身份验证(2FA)则是守护这把钥匙的保险箱。 1.1 高效通过GitHub学生认证 学生认证的核心在于向GitHub证明你当前的在读身份。整个过程需要细心,但绝非

By Ne0inhk

01 - 大模型推理框架选型入门:Ollama、llama.cpp与vLLM全景对比

01 - 大模型推理框架选型入门:Ollama、llama.cpp与vLLM全景对比 本文是《大模型推理框架深度解析》系列的第一篇,适合刚接触LLM部署的开发者阅读。 写在前面 随着大语言模型(LLM)的广泛应用,如何将模型高效地部署到生产环境成为每个AI工程师必须面对的问题。目前市面上主流的推理框架有Ollama、llama.cpp和vLLM,但它们的技术定位、适用场景差异巨大。 很多开发者在选型时容易陷入误区: * 用Ollama部署高并发API服务,结果吞吐量上不去 * 用vLLM跑边缘设备,发现资源占用过高 * 混淆llama.cpp和vLLM的定位,不知道何时该用哪个 本文将从架构分层视角出发,帮你建立清晰的选型认知。 一、三大框架的技术定位 1.1 三层架构视角 如果把LLM推理技术栈比作一座大厦,三个框架分别位于不同的楼层: ┌─────────────────────────────────────────────────────────────┐ │ 应用层(第3层) │ │ ┌─────────────┐ │ │ │ Ollama │

By Ne0inhk