一篇文章搞懂时间复杂度和空间复杂度

不知道小伙伴们有没有刷过力扣上的算法题,我在上研究生的时候,刷过了前40道题,上面的算法题,我觉得还挺难的,当你写完代码的时候,就可以提交自己写的代码到系统上,系统会给你写的代码计算时间复杂度和空间复杂度,并给出了你的排名。可能有的小伙伴不理解这个概念,今天我们来详细讲一下。

在编写和优化代码时,时间复杂度和空间复杂度是两个关键概念。它们帮助我们衡量算法的效率和资源使用情况,从而选择最优的解决方案。本文将详细探讨这两个概念,并通过具体的例子来说明它们在实际编程中的应用。

时间复杂度?

时间复杂度描述了一个算法的运行时间如何随着输入数据规模的增长而变化。它通常用大O符号表示,例如O(1)、O(n)、O(log n)等。以下是几种常见的时间复杂度类型及其含义:

O(1) - 常数时间复杂度
不管输入数据的大小如何,算法的执行时间始终相同。常数时间复杂度表示算法的运行时间不受输入数据规模的影响。

例子:

public int GetFirstElement(int[] array)
{
    return array[0];
}

解释:
这段代码总是返回数组的第一个元素,不管数组有多大,花费的时间都是一样的。这就像从一堆书里拿起最上面的一本书,不管书堆有多高,拿起一本书的时间始终不变。

示例图:

   函数调用:                    数组:
   GetFirstElement(array)        [10, 20, 30, 40, 50]
          |                       0   1   2   3   4
          |                           ↑
         返回 array[0]                |
                                      |
          O(1)  <--------------------+

注释:访问数组第一个元素的操作始终是常数时间,不受数组大小影响。

通过这种方式,我们可以清晰地展示O(1)时间复杂度的概念,即无论输入数据的大小如何,算法的执行时间始终相同。

O(n) - 线性时间复杂度
算法的执行时间与输入数据规模成正比。当输入数据增大时,运行时间也线性增长。

**例子:**求数组每个元素之和

public int Sum(int[] array)
{
    int sum = 0;
    foreach (int item in array)
    {
        sum += item;
    }
    return sum;
}

解释:
这个函数需要遍历整个数组来计算所有元素的总和。数组越大,需要的时间就越长。就像你需要逐个数数一堆硬币,硬币越多,数完的时间就越长。

示例图

scss复制代码函数调用:                       数组:
Sum(array)                       [10, 20, 30, 40, 50]
       |                           0   1   2   3   4
       |                           ↑   ↑   ↑   ↑   ↑
遍历每个元素  <------------------  |
                                     |
时间复杂度:O(n)  <---------------+

注释:计算数组所有元素的总和需要遍历整个数组,运行时间与数组大小成正比。

O(log n) - 对数时间复杂度
算法的执行时间随着输入数据规模的增长按对数比例增加。这通常出现在需要将数据集分成较小部分的算法中,例如二分查找。

**例子:**在有序数组中查找目标元素的索引

public int BinarySearch(int[] array, int target)
{
    int left = 0;
    int right = array.Length - 1;
    while (left <= right)
    {
        int mid = left + (right - left) / 2;
        if (array[mid] == target)
            return mid;
        if (array[mid] < target)
            left = mid + 1;
        else
            right = mid - 1;
    }
    return -1;
}

解释:
这个函数使用二分查找法来寻找目标元素。每次迭代都会把搜索范围缩小一半。这就像在一本按字母顺序排列的字典里查一个词,每次翻开中间的页面,决定目标词在前半部分还是后半部分,然后重复这个过程。

示例图

scss复制代码函数调用:                       数组:
BinarySearch(array, target)     [10, 20, 30, 40, 50]
       |                          0   1   2   3   4
       |                         第一次比较:
       |                         mid = 2 -> 30 == target ?
       |                         目标小于30,搜索范围缩小一半
       |                         新范围: [10, 20]
       |                                  0   1
       |                         第二次比较:
       |                         mid = 0 -> 10 == target ?
       |                         目标大于10,搜索范围缩小一半
       |                         新范围: [20]
       |                                  1
       |                         第三次比较:
       |                         mid = 1 -> 20 == target ?
       |                         找到目标,返回索引1
                                     ↑
                                     |
                                     |
          O(log n) <----------------+

注释:每次迭代搜索范围缩小一半,因此执行时间按对数比例增加。

O(n^2) - 平方时间复杂度
算法的执行时间与输入数据规模的平方成正比。这通常出现在嵌套循环中,每个循环遍历输入数据。

**例子:**将数组中的数据从小到大排序

public void BubbleSort(int[] array)
{
    for (int i = 0; i < array.Length - 1; i++)
    {
        for (int j = 0; j < array.Length - i - 1; j++)
        {
            if (array[j] > array[j + 1])
            {
                int temp = array[j];
                array[j] = array[j + 1];
                array[j + 1] = temp;
            }
        }
    }
}

解释:
这个函数使用冒泡排序算法对数组进行排序。它包含两个嵌套的循环,每个循环都会遍历数组。就像你要给一堆学生排座次,每次都要两两比较,整个过程需要很多次。

示例图

less复制代码函数调用:                        数组:
BubbleSort(array)                 [30, 10, 40, 20, 50]
   外层循环 i = 0:
       |                          第一次比较:30 > 10, 交换
       |                          新数组:[10, 30, 40, 20, 50]
   内层循环 j = 0:
       |                          第二次比较:30 > 40, 不交换
       |                          新数组:[10, 30, 40, 20, 50]
       |                          第三次比较:40 > 20, 交换
       |                          新数组:[10, 30, 20, 40, 50]
       |                          第四次比较:40 > 50, 不交换
   外层循环 i = 1:
       |                          第一次比较:10 > 30, 不交换
       |                          新数组:[10, 30, 20, 40, 50]
   内层循环 j = 1:
       |                          第二次比较:30 > 20, 交换
       |                          新数组:[10, 20, 30, 40, 50]
       |                          第三次比较:30 > 40, 不交换

   ... (持续进行直到排序完成)
                                     ↑
                                     |
                                     |
         O(n^2) <---------------------+

注释:每次外层循环遍历数组,内层循环也遍历数组,因此执行时间与数组大小的平方成正比。

O(2^n) - 指数时间复杂度
算法的执行时间随着输入数据规模的增长按指数比例增加。这种复杂度通常出现在递归算法中,尤其是解决组合问题时。

例子:

public int Fibonacci(int n)
{
    if (n <= 1)
        return n;
    return Fibonacci(n - 1) + Fibonacci(n - 2);
}

解释:
这个函数计算斐波那契数列的第n个数字。每次调用函数时,它会递归调用自身两次,导致总的调用次数按指数增长。

示例图

scss复制代码Fibonacci(5)
       |
   -------------
  |             |
Fib(4)        Fib(3)
  |             |
----         -----
|   |       |     |
Fib(3) Fib(2) Fib(2) Fib(1)
 |     |     |       |
---   ---   ---     ---
| |   | |   | |     |
Fib Fib Fib Fib Fib Fib
(2) (1) (1) (0) (1) (0)
 |
 ...
 
注释:每次调用函数时,它会递归调用自身两次,导致总的调用次数按指数增长,时间复杂度为 O(2^n)。
空间复杂度?

空间复杂度描述了一个算法在运行时需要的内存空间如何随着输入数据规模的增长而变化。以下是几种常见的空间复杂度类型及其含义:

O(1) - 常数空间复杂度
算法所需的内存空间不随输入数据规模的变化而变化。

例子:

public int GetFirstElement(int[] array)
{
    return array[0];
}

解释:
这个函数只返回数组的第一个元素,不需要额外的内存空间。就像你只拿一张桌子放书,无论书堆多高,只用一张桌子就够了。

示例图

scss复制代码函数调用:                      数组:
GetFirstElement(array)          [10, 20, 30, 40, 50]
       |                          0   1   2   3   4
       |                            ↑
返回 array[0]                        |
                                    |
        O(1)  <---------------------+

注释:访问数组第一个元素的操作不需要额外的内存空间,内存使用量不随输入数据规模变化。

O(n) - 线性空间复杂度
算法所需的内存空间与输入数据规模成正比。

例子:

public int[] CopyArray(int[] array)
{
    int[] newArray = new int[array.Length];
    for (int i = 0; i < array.Length; i++)
    {
        newArray[i] = array[i];
    }
}

解释:
这个函数创建了一个和输入数组一样大的新数组。就像你需要一个新的箱子来装一堆硬币,硬币越多,箱子就得越大。

示例图

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

O(n^2) - 平方空间复杂度
算法所需的内存空间与输入数据规模的平方成正比。

例子:

public int[,] CreateMatrix(int n)
{
    int[,] matrix = new int[n, n];
    return matrix;
}

解释:
这个函数创建了一个n x n的矩阵。随着n的增加,所需的内存空间按平方增长。

示例图

scss复制代码函数调用:                        矩阵:
CreateMatrix(n)                   n = 2
       |                          [1, 1]
       |                          [1, 1]
       |                          矩阵大小:2 x 2
       |
       |                          n = 3
       |                          [1, 1, 1]
       |                          [1, 1, 1]
       |                          [1, 1, 1]
       |                          矩阵大小:3 x 3
       |
       |                          n = 4
       |                          [1, 1, 1, 1]
       |                          [1, 1, 1, 1]
       |                          [1, 1, 1, 1]
       |                          [1, 1, 1, 1]
       |                          矩阵大小:4 x 4
                                     ↑
                                     |
                                     |
        O(n^2)  <---------------------+

注释:创建一个n x n的矩阵所需的内存空间随着n的平方增长。

O(2^n) - 指数空间复杂度
算法所需的内存空间随着输入数据规模的增长按指数比例增加。

**例子:**字符串的所有子集

public List<string> GenerateSubsets(string str)
{
    if (str.Length == 0)
    {
        return new List<string> { "" };
    }
    char first = str[0];
    List<string> subsets = GenerateSubsets(str.Substring(1));
    List<string> newSubsets = new List<string>();
    foreach (string subset in subsets)
    {
        newSubsets.Add(first + subset);
        newSubsets.Add(subset);
    }
    return newSubsets;
}

解释:
这个函数生成字符串的所有子集。随着字符串长度的增加,生成的子集数量按指数增长,需要的内存空间也按指数增长。

示例图

scss复制代码函数调用:
GenerateSubsets("abc")
       |
   ---------------
  |             |
GenSub("bc")  GenSub("bc")
  |             |
GenSub("c")   GenSub("c")
  |             |
GenSub("")    GenSub("")
  |             |
 [""]           [""]

 生成的子集:
 ["", "c", "b", "bc", "a", "ac", "ab", "abc"]
 
空间复杂度:O(2^n)

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

嵌入式c语言1——gcc以及linux嵌入式

GCC全名GNU Complier Collection&#xff0c;是一个开源的程序语言解释器&#xff0c;运行在linux系统中 对以程序名后缀结尾源代码文件&#xff0c;gcc可以做解释并生成可执行文件

uniapp做小程序内打开地图展示位置信息

使用场景&#xff1a;项目中需要通过位置信息打开地图查看当前位置信息在地图那个位置&#xff0c;每个酒店有自己的经纬度和详细地址&#xff0c;点击地图按钮打开内置地图如图 方法如下&#xff1a; <view class"dttu" click"openMap(info.locationY,info.…

解决Linux环境Qt报“cannot find -lgl“问题

今天&#xff0c;在Ubuntu 18.04.6环境下&#xff0c;安装Qt5.14.2之后&#xff0c;运行一个QWidget工程&#xff0c;发现Qt报"cannot find -lgl"错误。     出现这种现象的原因&#xff1a;Qt的Path路径没有配置&#xff0c;缺少libqt4-dev依赖包和一些必要的组件…

128陷阱详解

人不走空 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌赋&#xff1a;斯是陋室&#xff0c;惟吾德馨 目录 &#x1f308;个人主页&#xff1a;人不走空 &#x1f496;系列专栏&#xff1a;算法专题 ⏰诗词歌…

基于机器学习的永磁同步电机矢量控制策略-高分资源-下载可用!

基于机器学习的永磁同步电机矢量控制策略 优势 训练了RL-Agent&#xff0c;能够提高电机在非线性负载下的性能。 部分程序 仿真结果 转矩估计及dq轴电流。 代码有偿&#xff0c;50&#xff0c;需要的可以联系。

Vue前端练习

此练习项目只涉及前端&#xff0c;主要是vue和ElementUI框架的使用。&#xff08;ElementUI官网&#xff1a;Element - The worlds most popular Vue UI framework&#xff09; 一、环境准备 安装idea 安装Node.js 一键式安装(不需要做任何配置) npm -v&#xff08;也可用nod…

C语言 | Leetcode C语言题解之第198题打家劫舍

题目&#xff1a; 题解&#xff1a; int rob(int* nums, int numsSize){// dp0: 不偷这个屋子能窃到的最高金额int dp0 0;// dp1: 偷这间屋子能窃到的最高金额int dp1 nums[0];for (int i 1; i < numsSize; i) {int dp0new fmax(dp0, dp1);int dp1new dp0 nums[i];dp…

前端知识点

HTML、CSS 相关 1、 BFC 1、BFC 是什么&#xff1f; BFC&#xff08;Block Formatting Context&#xff09; 格式化上下文&#xff1b; 指一个独立的渲染区域&#xff0c;或者说是一个隔离的独立容器&#xff1b;可以理解为一个独立的封闭空间。无论如何不会影响到它的外面 …

day09了 加油

浅拷贝 指向同一个地址空间 右边不可取地址 左边一定是到了具体的位置 右值引用std&#xff1a;&#xff1a; move 相信大家默认构造函数都没有问题&#xff0c;所以就不贴例子了 浅拷贝构造函数 只负责复制地址&#xff0c;而不是真的把完整的内存给它 #include <iostre…

【MySQL备份】Percona XtraBackup基础篇

目录 1.关于Percona XtraBackup 2. Percona XtraBackup有哪些特点&#xff1f; 3.安装Percona XtraBackup 3.1.环境信息 3.2.安装步骤 4. xtrabackup内部流程图 5.Percona XtraBackup基础语法 5.1.全量备份 5.2.增量备份 5.2.1.基于全量备份的增量备份 5.2.2.基于前…

超越所有SOTA达11%!媲美全监督方法 | UC伯克利开源UnSAM

文章链接&#xff1a;https://arxiv.org/pdf/2406.20081 github链接&#xff1a;https://github.com/frank-xwang/UnSAM SAM 代表了计算机视觉领域&#xff0c;特别是图像分割领域的重大进步。对于需要详细分析和理解复杂视觉场景(如自动驾驶、医学成像和环境监控)的应用特别有…

深入解读OkHttp3中的Request5

OkHttp 是由 Square 开发的一个高效的 HTTP 客户端库&#xff0c;广泛应用于 Android 开发中。作为资深安卓开发工程师&#xff0c;我们经常需要与网络通信打交道&#xff0c;而 OkHttp 提供了一个简洁而强大的 API 来处理这些通信。在这篇文章中&#xff0c;我们将深入探讨 Ok…

python自动化运维--DNS处理模块dnspython

1.dnspython介绍 dnspython是Pyhton实现的一个DNS工具包&#xff0c;他几乎支持所有的记录类型&#xff0c;可以用于查询、传输并动态更新ZONE信息&#xff0c;同事支持TSIG&#xff08;事物签名&#xff09;验证消息和EDNS0&#xff08;扩展DNS&#xff09;。在系统管理方面&a…

EVE-NG网络仿真平台搭建

现在目前实验都是使用华为的Ensp模拟器&#xff0c;但是有时候一些功能模拟器无法实现&#xff0c;要不就是使用真机进行实验&#xff0c;第二个就是换个支持相关命令的模拟器了&#xff0c;今天来简单学习下EVE-NG这个模拟器。 一、EVE-NG简介 EVE-NG&#xff08;Emulated Vir…

【深度学习】注意力机制

https://blog.csdn.net/weixin_43334693/article/details/130189238 https://blog.csdn.net/weixin_47936614/article/details/130466448 https://blog.csdn.net/qq_51320133/article/details/138305880 注意力机制&#xff1a;在处理信息的时候&#xff0c;会将注意力放在需要…

HarmonyOS开发实战:UDP通讯示例规范

1. UDP简介 UDP协议是传输层协议的一种&#xff0c;它不需要建立连接&#xff0c;是不可靠、无序的&#xff0c;相对于TCP协议报文更简单&#xff0c;在特定场景下有更高的数据传输效率&#xff0c;在现代的网络通讯中有广泛的应用&#xff0c;以最新的HTTP/3为例&#xff0c;…

2024年6月29日 (周六) 叶子游戏新闻

老板键工具来唤去: 它可以为常用程序自定义快捷键&#xff0c;实现一键唤起、一键隐藏的 Windows 工具&#xff0c;并且支持窗口动态绑定快捷键&#xff08;无需设置自动实现&#xff09;。 喜马拉雅下载工具: 字面意思 《星刃》性感女主私密部位细节逼真 让玩家感到惊讶《星刃…

探索NVIDIA A100 显卡 如何手搓A100显卡

NVIDIA A100 显卡&#xff08;GPU&#xff09;是基于NVIDIA的Ampere架构设计的高性能计算和人工智能任务的处理器。 A100显卡主要由以下几种关键芯片和组件组成&#xff1a; 1. GPU芯片 NVIDIA GA100 GPU&#xff1a; 核心组件&#xff0c;是整个显卡的核心处理单元。GA100芯…

Ubuntu24.04 Isaacgym的安装

教程1 教程2 教程3 1.下载压缩包 link 2. 解压 tar -xvf IsaacGym_Preview_4_Package.tar.gz3. 从源码安装 Ubuntu24.04还需首先进入虚拟环境 python -m venv myenv # 创建虚拟环境&#xff0c;已有可跳过 source myenv/bin/activate # 激活虚拟环境python编译 cd isaa…

Python容器 之 字符串--字符串的常用操作方法

1.字符串查找方法 find() 说明&#xff1a;被查找字符是否存在于当前字符串中。 格式&#xff1a;字符串.find(被查找字符) 结果&#xff1a;如果存在则返回第一次出现 被查找字符位置的下标 如果不存在则返回 -1 需求&#xff1a; 1. 现有字符串数据: 我是中国人 2. 请设计程序…