深入理解算法效率:时间复杂度与空间复杂度

news2024/9/22 13:12:19

目录

引言

一、算法效率的基础

二、时间复杂度

1.概念

2.常见类型

1.O(1) — 常数阶 

2.O(n) — 线性阶

3.O(n^2) — 平方阶

4.O(2^𝑛) — 指数阶

5.O(log 𝑛) — 对数阶 

3.总结 

 三、空间复杂度

1.概念

2.常见类型

1.O(1) — 常数阶

2.O(n) — 线性阶

3.O(n^2) — 平方阶

四、总结


引言

在现代计算机科学和编程中,算法的效率至关重要。算法效率不仅影响程序的运行时间,还直接关系到程序的内存使用情况。为了评估和优化算法,我们常用两个主要指标:时间复杂度和空间复杂度。本文将详细介绍这两个概念,并通过C语言示例来解释它们的实际应用。

一、算法效率的基础

在算法设计中,我们先后追求以下两个层面的目标。

1. 找到问题解法:算法需要在规定的输入范围内可靠地求得问题的正确解。

2. 寻求最优解法:同一个问题可能存在多种解法,我们希望找到尽可能高效的算法。

也就是说,在能够解决问题的前提下,算法效率已成为衡量算法优劣的主要评价指标,它包括以下两个维度。

时间效率算法运行速度的快慢。

空间效率算法占用内存空间的大小。

简而言之,我们的目标是设计“既快又省”的数据结构与算法时间效率和空间效率的评估可以帮助我们选择合适的算法来处理特定问题,并优化程序性能。时间复杂度和空间复杂度是用于衡量这两个方面的关键指标。

二、时间复杂度

1.概念

时间复杂度(Time Complexity)用来衡量算法执行所需时间如何随着输入规模的增长而变化。它帮助我们评估算法在处理大数据量时的表现。时间复杂度通常用大O符号表示,描述了算法在最坏情况下的运行时间。

O的渐进表⽰法
⼤O符号(Big O notation):是⽤于描述函数渐进⾏为的数学符号
💡 推导⼤O阶规则
1. 时间复杂度函数式 T(N) 中,只保留最⾼阶项,去掉那些低阶项,因为当 N 不断变⼤时,
低阶项对结果影响越来越⼩,当 N ⽆穷⼤时,就可以忽略不计了。
2. 如果最⾼阶项存在且不是 1 ,则去除这个项⽬的常数系数,因为当 N 不断变⼤,这个系数
对结果影响越来越⼩,当 N ⽆穷⼤时,就可以忽略不计了。
3. T(N) 中如果没有 N 相关的项⽬,只有常数项,⽤常数 1 取代所有加法常数。

 

2.常见类型

1.O(1) — 常数阶 

常数阶时间复杂度指的是算法的运行时间与输入数据大小 𝑛 无关,即不随着 𝑛 的变化而变化。

在以下函数中,尽管操作数量 size 可能很大,但由于其与输入数据大小 𝑛 无关,因此时间复杂度仍为 𝑂(1)
/* 常数阶 */
int constant(int n) {
int count = 0;
int size = 100000;
int i = 0;
for (int i = 0; i < size; i++) {
count++;
}
return count; }

2.O(n) — 线性阶

线性阶时间复杂度指的是算法的运行时间随着输入规模n增加而以线性级别增长。

线性阶通常出现在单层循环中:
/* 线性阶 */
int linear(int n) {
	int count = 0;
	for (int i = 0; i < n; i++) {
		count++;
	}
	return count;
}

遍历数组和遍历链表等操作的时间复杂度均为 𝑂(𝑛) ,其中 𝑛 为数组或链表的长度:  

/* 线性阶(遍历数组) */
int arrayTraversal(int* nums, int n) {
	int count = 0;
	// 循环次数与数组长度成正比
	for (int i = 0; i < n; i++) {
		count++;
	}
	return count;
}

3.O(n^2) — 平方阶

平方阶时间复杂度指的是算法的运行时间随着输入规模n增加而以平方级别增长。

平方阶通常出现在嵌套循环中,外层循环和内层循环的时间复杂度都为 𝑂(𝑛) ,因此总体的时间复杂度为 𝑂(𝑛 2 )
/* 平方阶 */
int quadratic(int n) {
	int count = 0;
	// 循环次数与数据大小 n 成平方关系
	for (int i = 0; i < n; i++) {
		for (int j = 0; j < n; j++) {
			count++;
		}
	}
	return count;
}

4.O(2^𝑛) — 指数阶

指数阶时间复杂度指的是算法的运行时间随着输入规模n增加而以指数级别增长。
生物学的“细胞分裂”是指数阶增长的典型例子:初始状态为 1 个细胞,分裂一轮后变为 2 个,分裂两轮后变为 4 个,以此类推,分裂 𝑛 轮后有 2 𝑛 个细胞。

指数时间复杂度通常出现于解决组合问题或递归深度较大的算法:

/* 指数阶(递归实现) */
int expRecur(int n) {
    if (n == 1)
        return 1;
    return expRecur(n - 1) + expRecur(n - 1) + 1;
}

5.O(log 𝑛) — 对数阶 

对数阶时间复杂度指的是算法的运行时间随着输入规模n增加而以对数级别增长。

与指数阶类似,对数阶也常出现于递归函数中。以下代码形成了一棵高度为 log𝑛 的递归树:

/* 对数阶(递归实现) */
int logRecur(int n) {
	if (n <= 1)
		return 0;
	return logRecur(n / 2) + 1;
}
O(log 𝑛) 的底数是多少?
准确来说,“一分为 𝑚 ”对应的时间复杂度是 𝑂( log 𝑚 𝑛) 。而通过对数换底公式,我们可以得到具有不同底数、相等的时间复杂度:
也就是说,底数 𝑚 可以在不影响复杂度的前提下转换。因此我们通常会省略底数 𝑚 ,将对数阶直接记为 𝑂( log 𝑛)

3.总结 

设输入数据大小为 𝑛 ,常见的时间复杂度类型如图 2‑9 所示(按照从低到高的顺序排列)。
 O(1)  < O(log n)  < 𝑂(𝑛) O(n log n) <  O(2^𝑛)  <  O(2^𝑛) O(𝑛!)
常数阶 < 对数阶 < 线性阶 < 线性对数阶 < 平方阶 < 指数阶 < 阶乘阶

 三、空间复杂度

1.概念

空间复杂度(Space Complexity)衡量算法在执行过程中所需的额外内存空间如何随着输入规模的增长而变化。它描述了算法对内存的需求,通常也用大O符号表示。(这个概念与时间复杂度非常类似,只需将“运行时间”替换为“占用内存空间”。

2.常见类型

1.O(1) — 常数阶

常数空间复杂度表示算法所需的额外内存空间不随输入规模变化。例如,交换两个变量的值:
#include <stdio.h>

void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main() {
    int x = 10, y = 20;
    swap(&x, &y);
    printf("x = %d, y = %d\n", x, y);
    return 0;
}

2.O(n) — 线性阶

线性空间复杂度表示算法所需的额外内存空间与输入规模成正比。例如,创建一个数组:
#include <stdio.h>
#include <stdlib.h>

int *create_array(int size) {
    int *arr = (int *)malloc(size * sizeof(int));
    for (int i = 0; i < size; i++) {
        arr[i] = i;
    }
    return arr;
}

int main() {
    int size = 5;
    int *arr = create_array(size);
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    free(arr);
    return 0;
}

3.O(n^2) — 平方阶

平方空间复杂度表示算法的额外内存使用与输入规模的平方成正比。例如,创建一个二维矩阵:
#include <stdio.h>
#include <stdlib.h>

int **create_matrix(int n) {
    int **matrix = (int **)malloc(n * sizeof(int *));
    for (int i = 0; i < n; i++) {
        matrix[i] = (int *)malloc(n * sizeof(int));
    }
    return matrix;
}

void print_matrix(int **matrix, int n) {
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            printf("%d ", matrix[i][j]);
        }
        printf("\n");
    }
}

int main() {
    int n = 3;
    int **matrix = create_matrix(n);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            matrix[i][j] = i * n + j + 1;
        }
    }
    print_matrix(matrix, n);
    for (int i = 0; i < n; i++) {
        free(matrix[i]);
    }
    free(matrix);
    return 0;
}

四、总结

理解时间复杂度和空间复杂度是编写高效程序的基础。时间复杂度告诉我们算法的运行时间如何随输入规模变化,而空间复杂度则描述了算法对内存的需求。掌握这些概念可以帮助我们选择和优化算法,提高程序的性能。

希望本文能帮助你更好地理解算法复杂度。如果有任何问题或讨论,请在评论区留言!

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

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

相关文章

Linux进程(3)(进程优先级 - 优先级 - 命令行参数 - 环境变量)

目录 1.进程优先级 1&#xff09;什么是优先级 2&#xff09;为什么要有优先级 3&#xff09;Linux的优先级特点 && 查看方式 2.命令行参数和环境变量 3.环境变量 1&#xff09;直接现象 2&#xff09;见见更多的环境变量 3&#xff09;整体理解环境变量和系统…

【UEFI基础】BIOS模块执行的优先级

综述 BIOS下主要通过两种方式来确定一般模块的优先级&#xff0c;一种是fdf文件中指定的优先级&#xff0c;另一种是inf文件中指定的优先级。需要注意这里使用了“一般模块”的说法&#xff0c;因为有些模块&#xff08;尤其是PEI_CORE&#xff0c;DXE_CORE类型的模块&#xf…

Codeforces Round 972(Div.2)A+B

Codeforces Round 972&#xff08;Div.2&#xff09;ABC 昨天晚上做了Codeforces Round 972&#xff08;Div.2&#xff09;的A、B两道题&#xff0c;今天补一下思路。 题目来源&#xff1a;https://codeforces.com/contest/2005 A. Simple Palindrome 题目描述 输入输出样例…

linux---压缩打包

linux打包和压缩文件和目录&#xff1a; 归档(打包)命令&#xff1a;tar 归档就是将多个文件或者目录打包成为一个文件&#xff0c;存放再磁盘中&#xff0c;方便文件或者目录丢失时&#xff0c;可以恢复。 归档文件名使用相对路径 &#xff08;注意区分归档文件和被归档文…

Go 注册Nacos

根据需要GO 项目也接入Nacos 系统版本&#xff1a;Linux 5.4.18-87.76-generic KYLINOS SMP Thu Aug 31 09:05:44 UTC 2023 aarch64 aarch64 aarch64 GNU/Linux Go:1.19.4 1.查看Nacos-sdk-go 官方适配自己的工程 github.com/nacos-group/nacos-sdk-go NacosManager.go p…

【自动化测试】UI自动化的分类、如何选择合适的自动化测试工具以及其中appium的设计理念、引擎和引擎如何工作

引言 UI自动化测试主要针对软件的用户界面进行测试&#xff0c;以确保用户界面元素的交互和功能符合预期 文章目录 引言一、UI自动化的分类1.1 基于代码的自动化测试1.2 基于录制/回放的自动化测试1.3 基于框架的自动化测试1.4 按测试对象分类1.5 按测试层次分类1.6 按测试执行…

[产品管理-21]:NPDP新产品开发 - 19 - 产品设计与开发工具 - 详细设计与规格定义

目录 前言&#xff1a; 一、详细设计与规格定义概述 1、产品详细设计 2、规格定义 3、详细设计与规格定义的关系 4、实际应用中的注意事项 二、详细设计与规格定义主要工具 2.1 质量功能展开QFD - 需求跟踪矩阵 1、QFD的基本原理 2、QFD的实施步骤 3、QFD的优势与应…

智能赋能,Vatee万腾平台助力企业升级新高度

在当今这个日新月异的数字时代&#xff0c;智能技术的飞速发展正以前所未有的力量重塑着各行各业的面貌。作为这一变革浪潮中的佼佼者&#xff0c;Vatee万腾平台凭借其卓越的智能赋能能力&#xff0c;正引领众多企业迈向转型升级的新高度&#xff0c;开启了智能化发展的新篇章。…

【性能优化】分块

性能优化-tiling过程 这张图展示了在硬件和软件两个层面上执行矩阵乘法 C + = A B C += AB C+

560 和为k的子数组

解题思路&#xff1a; \qquad 一开始看到连续非空序列&#xff0c;会想到是不是可以用双指针表示一个区间&#xff0c;然后通过一次遍历找出所有可能的区间&#xff0c;但看到元素的取值区间就知道行不通&#xff0c;这个方法仅适用于数组元素大于等于0的情况。若数字是负数&a…

【Java面试】第十天

&#x1f31f;个人主页&#xff1a;时间会证明一切. 目录 Spring 中的 Bean 是线程安全的吗&#xff1f;有状态的Bean如何解决线程安全问题 SpringBoot和Spring的区别是什么&#xff1f;SpringBoot的启动流程是怎么样的&#xff1f;new SpringApplication()SpringApplication.r…

【Android 13源码分析】WindowContainer窗口层级-2-构建流程

在安卓源码的设计中&#xff0c;将将屏幕分为了37层&#xff0c;不同的窗口将在不同的层级中显示。 对这一块的概念以及相关源码做了详细分析&#xff0c;整理出以下几篇。 【Android 13源码分析】WindowContainer窗口层级-1-初识窗口层级树 【Android 13源码分析】WindowCon…

ArrayList 源码解析

ArrayList是Java集合框架中的一个动态数组实现&#xff0c;提供了可变大小的数组功能。它继承自AbstractList并实现了List接口&#xff0c;是顺序容器&#xff0c;即元素存放的数据与放进去的顺序相同&#xff0c;允许放入null元素&#xff0c;底层通过数组实现。除该类未实现同…

模板替换引擎(支持富文本动态表格)

模板替换引擎&#xff08;支持富文本动态表格&#xff09; 前言功能介绍example&#xff1a; 使用方法函数扩展系统函数自定义函数 前言 分享一下自己开源的工具——模板替换引擎 https://github.com/RwTo/template-engine 可以拿来学习设计模式或使用 感兴趣的话&#xff…

网络编程基础概述

文章目录 协议网络协议栈(osi)局域网IPIP和Mac地址端口号TCP和UDP网络字节序 协议 (网络协议的)意义:为了让计算机传输之间将信息正确传输给目标机器 不同系统之间能接入网络是因为定制了一套通用的协议以便支持不同系统间的网络通信 1.网络通信的问题: 将数据可靠的从A传给B a…

AtCoder ABC369 A-D题解

比赛链接:ABC369 省流&#xff1a;A<B<D<C&#xff08;题解是按照该顺序写的&#xff09; Problem A: #include <bist/stdc.h> using namespace std; int main(){int A,B;cin>>A>>B;if(AB)cout<<1<<endl;else if(abs(A-B)%20)cout&l…

织物缺陷检测系统源码分享

织物缺陷检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vis…

1405 问题 E: 世界杯

废话 这个题&#xff0c;我估计 22 22 22 年的时候写过一次&#xff0c;当时应该是搞明白了&#xff0c;现在重新写还是不会写&#xff0c;有点无奈 题目 问题 E: 世界杯&#xff1a;现在的 OJ 把题目加到一个活动里面去之后&#xff0c;感觉之后这个链接就访问不了了。题目…

伙房食堂电气安全新挑战:油烟潮湿环境下,如何筑起电气火灾“防火墙”?

近几年&#xff0c;随着我国经济的飞速发展&#xff0c;食堂餐饮也经历了一场变革&#xff0c;越来越多的电器走进了伙房食堂中&#xff0c;实现了电气化&#xff0c;为人们提供了高效便利的饮食服务&#xff0c;但同时也增加了火灾负荷。目前我国非常严重的电气火灾危害&#…

【STM32】DAC数字模拟转换

本篇博客重点在于标准库函数的理解与使用&#xff0c;搭建一个框架便于快速开发 目录 前言 DAC简介 DAC配置 DAC时钟使能 GPIO初始化 DAC配置 DAC使能 读写DAC值 驱动代码 MyDAC.h MyDAC.c main.c 前言 大容量的STM32F101xx和STM32F103xx产品才有DAC外设 大容量…