C语言【数据结构】:时间复杂度和空间复杂度.详解
引言
详细介绍什么是时间复杂度和空间复杂度。
前言:为什么要学习时间复杂度和空间复杂度
算法在编写成可执行程序后,运行时需要耗费时间资源和空间(内存)资源。因此衡量一个算法的好坏,一般是从时间和空间两个维度来衡量的,即时间复杂度和空间复杂度。
时间复杂度主要衡量一个算法的运行快慢,而空间复杂度主要衡量⼀个算法运行所需要的额外空间。 在计算机发展的早期,计算机的存储容量很小。所以对空间复杂度很是在乎。但是经过计算机行业的迅速发展,计算机的存储容量已经达到了很高的程度。所以我们如今已经不需要再特别关注⼀个算法的空间复杂度。
一、时间复杂度
问:时间复杂度是程序运行的时间吗?
这是一个很严重的问题。这里先肯定回答:不是。
仔细往下看,最后你亲自来回答这个问题
1.什么是时间复杂度
时间复杂度是对一个算法运行时间长短的一种度量,它描述的是随着输入数据规模
n的增大,算法执行时间的增长趋势,而不是具体的运行时间。因为具体运行时间会受到硬件、编程语言、编译器等多种因素的影响,所以使用时间复杂度可以更客观地评估算法的效率。

程序执行的时间 = 二进制指令运行的时间 * 执行的次数
因为程序在计算机中二进制指令的运行时间是非常快的,可以假定时间是一样的,所以使用代码的执行次数来等效程序的执行时间
2.表示方法(大O表示法)
通常使用大 O 符号(Big O notation)来表示时间复杂度。大 O 符号表示的是算法执行时间的上界,即最坏情况下的时间复杂度。看到这里你可能不知道什么是大O表示法,跟随下面的案例来理解,就明白什么是大O表示法了。
推导大O阶规则
1. 时间复杂度函数式 T(N) 中,只保留最高阶项,去掉那些低阶项,因为当 N 不断变⼤时, 低阶项对结果影响越来越⼩,当 N 无穷大时,就可以忽略不计了。
2. 如果最高阶项存在且不是 1 ,则去除这个项目的常数系数,因为当 N 不断变大,这个系数对结果影响越来越小,当 N 无穷大时,就可以忽略不计了。
3.T(N) 中如果没有 N 相关的项目,只有常数项,用常数 1 取代所有加法常数。
3.常见的时间复杂度
1. O(1):常数时间复杂度
代码一:
推导一下这段代码的执行次数T与n之间的函数关系
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);
}
T(N)= 2 * N + 10;
当N足够大时,从1 增长到10000000,会发现2 和10 对次数的影响不是很大,所以
1. 如果最高阶项存在且不是 1 ,则去除这个项目的常数系数,因为当 N 不断变大,这个系数对结果影响越来越小,当 N 无穷大时,就可以忽略不计了。
2. T(N) 中如果没有 N 相关的项目,只有常数项,用常数 1 取代所有加法常数。
代码二:
void Func4(int N)
{int count = 0;for (int k = 0; k < 100; ++k){++count;}printf("%d\n", count);
}
观察这段代码,会发现n和程序的执行次数是没有关系的(T(N)= 100),这时就认为时间复杂度为O(1);
2. O(n):线性时间复杂度
代码一:
算法的执行时间与输入数据规模 n 成正比。
#include <stdio.h>// 计算数组元素的和
int sum(int arr[], int n) {int total = 0;for (int i = 0; i < n; i++) {total += arr[i];}return total;
}int main() {int arr[] = {1, 2, 3, 4, 5};int n = sizeof(arr) / sizeof(arr[0]);int result = sum(arr, n);printf("Sum = %d\n", result);return 0;
}
在
sum函数中,需要遍历数组中的每个元素,因此循环的执行次数与数组的长度n成正比,时间复杂度为 O(n)。即因为是要循环n次,所以是O(n)。
代码二:
const char* strchr(const char* str, int character)
{const char* p_begin = 's';while (*p_begin != character){if (*p_begin == '\0')return NULL;p_begin++;}return p_begin;
strchr执行的基本操作次数:
1)若要查找的字符在字符串第⼀个位置,则: T(N) = 1
2)若要查找的字符在字符串最后的⼀个位置, 则: T(N) = N
3)若要查找的字符在字符串中间位置,则:N T(N) = 2 因此:strchr的时间复杂度分为: 最好情况: O(1)
最坏情况: O(N)
平均情况: O(N)
通过上面我们会发现,有些算法的时间复杂度存在最好、平均和最坏情况。最坏情况:任意输入规模的最大运行次数(上界)
平均情况:任意输入规模的期望运行次数
最好情况:任意输入规模的最小运行次数(下界)
大O的渐进表示法在实际中⼀般情况关注的是算法的上界,也就是最坏运行情况。
所以上面这段代码的时间复杂度是O(N)
3. O(n^2):平方时间复杂度
算法的执行时间与输入数据规模 n 的平方成正比。
参考代码:
#include <stdio.h>// 冒泡排序
void bubbleSort(int arr[], int n) {for (int i = 0; i < n - 1; i++) {for (int j = 0; j < n - i - 1; j++) {if (arr[j] > arr[j + 1]) {int temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;}}}
}int main() {int arr[] = {64, 34, 25, 12, 22, 11, 90};int n = sizeof(arr) / sizeof(arr[0]);bubbleSort(arr, n);for (int i = 0; i < n; i++) {printf("%d ", arr[i]);}printf("\n");return 0;
}
粗略理解:在
bubbleSort函数中,使用了两层嵌套循环,因此时间复杂度为 O(n^2)。细节分析:
1)若数组有序,则: T(N)=N
2)若数组有序且为降序,则: T(N)= 1 + 2 + 3 + .......+ n - 1 = ((n - 1) * (1 + n - 1) ) / 2 即等差数列求和公式:a1 = 1, an = n - 1, 共n - 1项。
T(N)= (n^2) * 1 / 2 + n / 2;
因此:BubbleSort的时间复杂度取最差情况为: O(N ^ 2)
4. O(log n ):复杂度
参考代码:
void func5(int n)
{int cnt = 1;while (cnt < n){cnt *= 2;}
}
当n= 2 时,执行次数为1
当n= 4 时,执行次数为2
当n=16时,执行次数为4
假设执行次数为x,则 2^x = x 因此执行次数: x=logn
因此:func5的时间复杂度取最差情况为:O(log n)
这里为什么不写
因为这个在输入法上不好打出来
注意:在有的地方会把 logn 写成lgn,严格上来说这个是不对的
当n接近无穷大时,底数的大小对结果影响不大。因此,一般情况下不管底数是多少都可以省略不写。
还有其他的时间复杂度如:n*logn, n!(n的阶乘),在以后的学习中肯定会遇到
二、空间复杂度
1.什么是空间复杂度
定义
空间复杂度是对一个算法在运行过程中临时占用存储空间大小的一种度量,它描述的是随着输入数据规模 n 的增大,算法所需额外存储空间的增长趋势。
注意:函数栈帧的创建和销毁是不算入空间复杂度的,即 创建函数 和 销毁函数 是不算入时间复杂度的。
表示方法
同样使用大 O 符号来表示空间复杂度。
常见的空间复杂度及其示例代码
1. O(1):常数空间复杂度
算法在运行过程中所需的额外存储空间是固定的,不随输入数据规模 n 的变化而变化。
#include <stdio.h>// 计算数组元素的和
int sum(int arr[], int n) {int total = 0;for (int i = 0; i < n; i++) {total += arr[i];}return total;
}int main() {int arr[] = {1, 2, 3, 4, 5};int n = sizeof(arr) / sizeof(arr[0]);int result = sum(arr, n);printf("Sum = %d\n", result);return 0;
}
在 sum 函数中,只使用了一个额外的变量
total来存储数组元素的和,因此空间复杂度为 O(1)。
2. O(n):线性空间复杂度
算法在运行过程中所需的额外存储空间与输入数据规模 n 成正比。
#include <stdio.h>
#include <stdlib.h>// 复制数组
int* copyArray(int arr[], int n) {int *newArr = (int*)malloc(n * sizeof(int));for (int i = 0; i < n; i++) {newArr[i] = arr[i];}return newArr;
}int main() {int arr[] = {1, 2, 3, 4, 5};int n = sizeof(arr) / sizeof(arr[0]);int *newArr = copyArray(arr, n);for (int i = 0; i < n; i++) {printf("%d ", newArr[i]);}printf("\n");free(newArr);return 0;
}
在copyArray 函数中,使用了 malloc 函数动态分配了一个大小为
n的数组,因此空间复杂度为 O(n)。
5. 常见复杂度对比

在计算机发展的早期,计算机的存储容量很小。所以对空间复杂度很是在乎。但是经过计算机行业的迅速发展,计算机的存储容量已经达到了很高的程度。所以我们如今已经不需要再特别关注⼀个算法的空间复杂度。
但是,在算法竞赛中,往往需要有一种思想:用时间换空间,或者用空间换时间。
相关文章:
C语言【数据结构】:时间复杂度和空间复杂度.详解
引言 详细介绍什么是时间复杂度和空间复杂度。 前言:为什么要学习时间复杂度和空间复杂度 算法在编写成可执行程序后,运行时需要耗费时间资源和空间(内存)资源。因此衡量一个算法的好坏,一般是从时间和空间两个维度来衡量的,即时…...
大模型的参数数量与学习的知识数量之间
大模型的参数数量与学习的知识数量之间 大模型的参数数量与学习的知识数量之间呈现非线性、条件依赖的复杂关系,其本质是**「表达能力」与「知识编码效率」的动态博弈**。以下从五个维度拆解核心逻辑: 一、参数是知识的「载体容量」,但非唯一决定因素 理论上限:参数数量决…...
基于Python的selenium入门超详细教程(第2章)--单元测试框架unittest
学习路线 自动化测试介绍及学习路线-CSDN博客 自动化测试之Web自动化(基于pythonselenium)-CSDN博客 基于Python的selenium入门超详细教程(第1章)--WebDriver API篇-CSDN博客 目录 前言: 一、单元测试 1. 单元测试的定义 2. 单元测…...
日志、类加载器、XML(配置文件)
目录 一、日志1.日志技术的概述2.日志技术的体系a. Logback 3.日志的级别 二、类加载器1.概述2.类加载时机3.类加载过程3.类加载器的分类4.常用方法 三、XML(配置文件)1.概述2.XML的基本语法3.XML的文档约束a.DTD约束b.schema约束 4.XML文档解析a.Dom4jb…...
Flutter中的const和final的区别
目录 一、核心区别对比表 二、初始化机制深度解析 1. const 的编译期特性 2. final 的运行时特性 三、内存管理差异 1. const 的内存优化 2. final 的独立内存 四、集合类型的本质区别 1. const 集合的完全不可变性 2. final 集合的引用不可变性 五、在 Flutter 中的…...
DAY34 贪心算法Ⅲ
134. 加油站 - 力扣(LeetCode) 这种环路问题要记一下。 class Solution { public:int canCompleteCircuit(vector<int>& gas, vector<int>& cost) {int curSum0;int totalSum0;int start0;for(int i0;i<gas.size();i){curSumga…...
AI大白话(一):5分钟了解AI到底是什么?
🌟引言: 在这个信息爆炸的时代,“人工智能”、“AI”、“机器学习”、"深度学习"等词汇频繁出现在我们的生活中。 从手机里的语音助手,到网购平台的个性化推荐,再到最近大火的AI绘画和ChatGPT,人…...
(七)Spring Boot学习——Redis使用
有部分内容是常用的,为了避免每次都查询数据库,将部分数据存入Redis。 一、 下载并安装 Redis Windows 版的 Redis 官方已不再维护,你可以使用 微软提供的 Redis for Windows 版本 或者 使用 WSL(Windows Subsystem for Linux&a…...
蓝桥与力扣刷题(蓝桥 字符统计)
题目:给定一个只包含大写字母的字符出 S, 请你输出其中出现次数最多的字符。如果有多个字母均出现了最多次, 按字母表顺序依次输出所有这些字母。 输入格式 一个只包含大写字母的字等串 S. 输出格式 若干个大写字母,代表答案。 样例输入 BABBACAC样…...
AtCoder Beginner Contest 397(ABCDE)
目录 A - Thermometer 翻译: 思路: 实现: B - Ticket Gate Log 翻译: 思路: 实现: C - Variety Split Easy 翻译: 思路: 实现: D - Cubes 翻译:…...
Profinet转Profinet以创新网关模块为核心搭建西门子和欧姆龙PLC稳定通讯架构案例
你是否有听过PROFINET主站与PROFINET主站之间需要做数据通讯有需求? 例如西门子1500与霍尼韦尔DCS系统两个主站之间的通讯。应用于PROFINET为主站设备还有欧姆龙、基恩士、罗克韦尔、施耐德、GE、ABB等品牌的PLC或DCS、FCS等平台。在生产或智能领域有通讯需求。两头…...
计算机视觉|Swin Transformer:视觉 Transformer 的新方向
一、引言 在计算机视觉领域的发展历程中,卷积神经网络(CNN) 长期占据主导地位。从早期的 LeNet 到后来的 AlexNet、VGGNet、ResNet 等,CNN 在图像分类、目标检测、语义分割等任务中取得了显著成果。然而,CNN 在捕捉全…...
C++单例模式精解
单例模式(重点*) 单例模式是23种常用设计模式中最简单的设计模式之一,它提供了一种创建对象的方式,确保只有单个对象被创建。这个设计模式主要目的是想在整个系统中只能出现类的一个实例,即一个类只有一个对象。 将单…...
【java】集合练习2
Student.java:保存学生类的定义。 public class Student {private String name;private int age;public Student(String name, int age) {this.name name;this.age age;}public String getName() { return name; }public int getAge() { return age; }Overridepu…...
FineBI_实现求当日/月/年回款金额分析
需求:原始数据结构如下,需要在分组表中,实现各城市当日/月/年的合同金额分析 实现步骤: ①维度拖入城市 ②分别取当日/月/年合同金额 当日DEF(SUM_AGG(${ 地区数据分析1 _ 合同金额 }),[${ 地区数据分析1 _ 城市 }],[LEFT(${ 地…...
【计算机网络】2物理层
物理层任务:实现相邻节点之间比特(或)的传输 1.通信基础 1.1.基本概念 1.1.1.信源,信宿,信道,数据,信号 数据通信系统主要划分为信源、信道、信宿三部分。 信源:产生和发送数据的源头。 信宿:接收数据的终点。 信道:信号的传输介质。 数据和信号都有模拟或数字…...
解决PC串流至IPad Pro时由于分辨率不一致导致的黑边问题和鼠标滚轮反转问题
问题背景 今天在做 电脑串流ipad pro 的时候发现了2个问题: 1.ipadpro 接上鼠标后,滚轮上下反转,这个是苹果自己的模拟造成的问题,在设置里选择“触控板与鼠标”。 关闭“自然滚动”,就可以让鼠标滚轮正向滚动。 2. ipadpro 分…...
在办公电脑上本地部署 70b 的 DeepSeek 模型并实现相应功能的大致步骤
以下是为客户在办公电脑上本地部署 70b 的 DeepSeek 模型并实现相应功能的大致步骤: 硬件准备: 70b 模型对硬件要求较高,确保办公电脑有足够强大的 GPU(例如 NVIDIA A100 等高端 GPU,因为模型规模较大,普通…...
LLMs之CoD:《Chain of Draft: Thinking Faster by Writing Less》翻译与解读
LLMs之CoD:《Chain of Draft: Thinking Faster by Writing Less》翻译与解读 导读:这篇论文的核心是提出了一种名为“Chain of Draft”(CoD,草稿链)的新型提示策略,用于改进大型语言模型(LLMs&a…...
Docker安装mysql——Linux系统
拉取mysql镜像 docker pull mysql 查看镜像 docker images 运行镜像(这一步的作用:数据持久化,通过挂载卷将日志、数据和配置文件存储在主机上,避免容器删除导致数据丢失) docker run -p 3306:3306 --name mysql …...
0CTF 2016 piapiapia 1
#源码泄露 #代码审计 #反序列化字符逃逸 #strlen长度过滤数组绕过 www.zip 得到源码 看到这里有flag ,猜测服务端docker的主机里,$flag变量应该存的就是我们要的flag。 于是,我们的目的就是读取config.php 利用思路 这里存在 任意文件读取…...
2、危机应对-核心成员突然退出
一、场景: 当你团队中的骨干突然退出项目,如开发主程不干了,交付经理如何应对? 二、思考: 处理核心成员退出的本质是“通过系统性的减震降低人岗绑定的风险” 三、处理方式: 1、紧急评估影响 技术影响…...
python_巨潮年报pdf下载
目录 前置: 步骤: step one: pip安装必要包,获取年报url列表 step two: 将查看url列表转换为pdf url step three: 多进程下载pdf 前置: 1 了解一些股票的基本面需要看历年年报,在巨潮一个个下载比较费时间&…...
单片机自学指南
一、单片机基础入门 单片机的概念与发展历程 常见单片机类型介绍(如 51 系列、STM32 系列等) 单片机在生活与工业中的应用实例剖析 二、硬件原理学习 单片机内部结构详解(CPU、存储器、I/O 口等) 时钟电路与复位电路原理 电…...
Netty基础—6.Netty实现RPC服务三
大纲 1.RPC的相关概念 2.RPC服务调用端动态代理实现 3.Netty客户端之RPC远程调用过程分析 4.RPC网络通信中的编码解码器 5.Netty服务端之RPC服务提供端的处理 6.RPC服务调用端实现超时功能 5.Netty服务端之RPC服务提供端的处理 (1)RPC服务提供端NettyServer (2)基于反射…...
用vue3显示websocket的状态
在上次vue3项目上增加一个标签,显示当前的连接状态,两个按钮:重新连接 和 断开连接 修改App.vue <template><header><title>ws状态测试</title></header><main><WsStatus /></main> </template>…...
python拉取大视频导入deepseek大模型解决方案
使用Python拉取大视频并导入大模型,需要综合考虑数据获取、存储、处理和资源管理,确保高效稳定地处理大视频数据,同时充分利用大模型的性能,以下是分步方案及代码示例: --- 1. 分块下载大视频(避免内存溢出…...
为什么需要使用十堰高防服务器?
十堰高防服务器的核心价值与应用必要性 一、应对复杂攻击的防御能力 T级DDoS攻击防护 十堰高防服务器搭载 T级清洗中心,支持智能流量调度与分层处理,可抵御 800Gbps-1.2Tbps 的大规模混合攻击(如SYN Flood、UDP反射ÿ…...
[特殊字符] 深度实战:Android 13 系统定制之 Recovery 模式瘦身指南
🌟 核心需求 在 Android 13 商显设备开发中,需精简 Recovery 模式的菜单选项(如Reboot to bootloader/Enter rescue),但直接修改g_menu_actions后在User 版本出现黑屏卡死问题,需综合方案解决。 ǵ…...
向量数据库技术系列四-FAISS介绍
一、前言 FAISS(Facebook AI Similarity Search)是由Facebook AI Research开发的一个开源库,主要用于高效地进行大规模相似性搜索和聚类操作。主要功能如下: 向量索引与搜索:FAISS提供了多种索引和搜索向量的方法&…...

