C语言【数据结构】:时间复杂度和空间复杂度.详解

news2025/3/17 14:17:11

引言

        详细介绍什么是时间复杂度和空间复杂度。

前言:为什么要学习时间复杂度和空间复杂度

        算法在编写成可执行程序后,运行时需要耗费时间资源和空间(内存)资源。因此衡量一个算法的好坏,一般是从时间和空间两个维度来衡量的,即时间复杂度和空间复杂度。

        时间复杂度主要衡量一个算法的运行快慢,而空间复杂度主要衡量⼀个算法运行所需要的额外空间。 在计算机发展的早期,计算机的存储容量很小。所以对空间复杂度很是在乎。但是经过计算机行业的迅速发展,计算机的存储容量已经达到了很高的程度。所以我们如今已经不需要再特别关注⼀个算法的空间复杂度。

一、时间复杂度 

问:时间复杂度是程序运行的时间吗?                     

        这是一个很严重的问题。这里先肯定回答:不是

仔细往下看,最后你亲自来回答这个问题                                

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. 常见复杂度对比

  

       在计算机发展的早期,计算机的存储容量很小。所以对空间复杂度很是在乎。但是经过计算机行业的迅速发展,计算机的存储容量已经达到了很高的程度。所以我们如今已经不需要再特别关注⼀个算法的空间复杂度。

        但是,在算法竞赛中,往往需要有一种思想:用时间换空间,或者用空间换时间。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于Python的selenium入门超详细教程(第2章)--单元测试框架unittest

学习路线 自动化测试介绍及学习路线-CSDN博客 ​自动化测试之Web自动化&#xff08;基于pythonselenium&#xff09;-CSDN博客 基于Python的selenium入门超详细教程(第1章)--WebDriver API篇-CSDN博客 目录 前言&#xff1a; 一、单元测试 1. 单元测试的定义 2. 单元测…

日志、类加载器、XML(配置文件)

目录 一、日志1.日志技术的概述2.日志技术的体系a. Logback 3.日志的级别 二、类加载器1.概述2.类加载时机3.类加载过程3.类加载器的分类4.常用方法 三、XML&#xff08;配置文件&#xff09;1.概述2.XML的基本语法3.XML的文档约束a.DTD约束b.schema约束 4.XML文档解析a.Dom4jb…

AI大白话(一):5分钟了解AI到底是什么?

&#x1f31f;引言&#xff1a; 在这个信息爆炸的时代&#xff0c;“人工智能”、“AI”、“机器学习”、"深度学习"等词汇频繁出现在我们的生活中。 从手机里的语音助手&#xff0c;到网购平台的个性化推荐&#xff0c;再到最近大火的AI绘画和ChatGPT&#xff0c;人…

蓝桥与力扣刷题(蓝桥 字符统计)

题目&#xff1a;给定一个只包含大写字母的字符出 S, 请你输出其中出现次数最多的字符。如果有多个字母均出现了最多次, 按字母表顺序依次输出所有这些字母。 输入格式 一个只包含大写字母的字等串 S. 输出格式 若干个大写字母&#xff0c;代表答案。 样例输入 BABBACAC样…

AtCoder Beginner Contest 397(ABCDE)

目录 A - Thermometer 翻译&#xff1a; 思路&#xff1a; 实现&#xff1a; B - Ticket Gate Log 翻译&#xff1a; 思路&#xff1a; 实现&#xff1a; C - Variety Split Easy 翻译&#xff1a; 思路&#xff1a; 实现&#xff1a; D - Cubes 翻译&#xff1a…

Profinet转Profinet以创新网关模块为核心搭建西门子和欧姆龙PLC稳定通讯架构案例​

你是否有听过PROFINET主站与PROFINET主站之间需要做数据通讯有需求&#xff1f; 例如西门子1500与霍尼韦尔DCS系统两个主站之间的通讯。应用于PROFINET为主站设备还有欧姆龙、基恩士、罗克韦尔、施耐德、GE、ABB等品牌的PLC或DCS、FCS等平台。在生产或智能领域有通讯需求。两头…

计算机视觉|Swin Transformer:视觉 Transformer 的新方向

一、引言 在计算机视觉领域的发展历程中&#xff0c;卷积神经网络&#xff08;CNN&#xff09; 长期占据主导地位。从早期的 LeNet 到后来的 AlexNet、VGGNet、ResNet 等&#xff0c;CNN 在图像分类、目标检测、语义分割等任务中取得了显著成果。然而&#xff0c;CNN 在捕捉全…

C++单例模式精解

单例模式&#xff08;重点*&#xff09; 单例模式是23种常用设计模式中最简单的设计模式之一&#xff0c;它提供了一种创建对象的方式&#xff0c;确保只有单个对象被创建。这个设计模式主要目的是想在整个系统中只能出现类的一个实例&#xff0c;即一个类只有一个对象。 将单…

【java】集合练习2

Student.java&#xff1a;保存学生类的定义。 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_实现求当日/月/年回款金额分析

需求&#xff1a;原始数据结构如下&#xff0c;需要在分组表中&#xff0c;实现各城市当日/月/年的合同金额分析 实现步骤&#xff1a; ①维度拖入城市 ②分别取当日/月/年合同金额 当日DEF(SUM_AGG(${ 地区数据分析1 _ 合同金额 }),[${ 地区数据分析1 _ 城市 }],[LEFT(${ 地…

【计算机网络】2物理层

物理层任务:实现相邻节点之间比特(或)的传输 1.通信基础 1.1.基本概念 1.1.1.信源,信宿,信道,数据,信号 数据通信系统主要划分为信源、信道、信宿三部分。 信源:产生和发送数据的源头。 信宿:接收数据的终点。 信道:信号的传输介质。 数据和信号都有模拟或数字…

解决PC串流至IPad Pro时由于分辨率不一致导致的黑边问题和鼠标滚轮反转问题

问题背景 今天在做 电脑串流ipad pro 的时候发现了2个问题&#xff1a; 1.ipadpro 接上鼠标后&#xff0c;滚轮上下反转&#xff0c;这个是苹果自己的模拟造成的问题&#xff0c;在设置里选择“触控板与鼠标”。 关闭“自然滚动”,就可以让鼠标滚轮正向滚动。 2. ipadpro 分…

LLMs之CoD:《Chain of Draft: Thinking Faster by Writing Less》翻译与解读

LLMs之CoD&#xff1a;《Chain of Draft: Thinking Faster by Writing Less》翻译与解读 导读&#xff1a;这篇论文的核心是提出了一种名为“Chain of Draft”&#xff08;CoD&#xff0c;草稿链&#xff09;的新型提示策略&#xff0c;用于改进大型语言模型&#xff08;LLMs&a…

0CTF 2016 piapiapia 1

#源码泄露 #代码审计 #反序列化字符逃逸 #strlen长度过滤数组绕过 www.zip 得到源码 看到这里有flag &#xff0c;猜测服务端docker的主机里&#xff0c;$flag变量应该存的就是我们要的flag。 于是&#xff0c;我们的目的就是读取config.php 利用思路 这里存在 任意文件读取…

python_巨潮年报pdf下载

目录 前置&#xff1a; 步骤&#xff1a; step one: pip安装必要包&#xff0c;获取年报url列表 step two: 将查看url列表转换为pdf url step three: 多进程下载pdf 前置&#xff1a; 1 了解一些股票的基本面需要看历年年报&#xff0c;在巨潮一个个下载比较费时间&…

为什么需要使用十堰高防服务器?

十堰高防服务器的核心价值与应用必要性 一、‌应对复杂攻击的防御能力‌ ‌T级DDoS攻击防护‌ 十堰高防服务器搭载 ‌T级清洗中心‌&#xff0c;支持智能流量调度与分层处理&#xff0c;可抵御 ‌800Gbps-1.2Tbps‌ 的大规模混合攻击&#xff08;如SYN Flood、UDP反射&#xff…

人工智能中的线性代数基础详解

‌ 线性代数是人工智能领域的重要数学基础之一,是人工智能技术的底层数学支柱,它为数据表示、模型构建和算法优化提供了核心工具。其核心概念与算法应用贯穿数据表示、模型训练及优化全过程。更多内容可看我文章:人工智能数学基础详解与拓展-CSDN博客 一、基本介绍 …

【毕业论文格式】word分页符后的标题段前间距消失

文章目录 【问题描述】 分页符之后的段落开头&#xff0c;明明设置了标题有段前段后间距&#xff0c;但是没有显示间距&#xff1a; 【解决办法】 选中标题&#xff0c;选择边框 3. 选择段前间距&#xff0c;1~31磅的一个数 结果

【蓝桥杯每日一题】3.16

&#x1f3dd;️专栏&#xff1a; 【蓝桥杯备篇】 &#x1f305;主页&#xff1a; f狐o狸x 目录 3.9 高精度算法 一、高精度加法 题目链接&#xff1a; 题目描述&#xff1a; 解题思路&#xff1a; 解题代码&#xff1a; 二、高精度减法 题目链接&#xff1a; 题目描述&…

2.7 滑动窗口专题:串联所有单词的子串

LeetCode 30. 串联所有单词的子串算法对比分析 1. 题目链接 LeetCode 30. 串联所有单词的子串 2. 题目描述 给定一个字符串 s 和一个字符串数组 words&#xff0c;words 中所有单词长度相同。要求找到 s 中所有起始索引&#xff0c;使得从该位置开始的连续子串包含 words 中所…