C++时间复杂度与空间复杂度

news2024/11/21 23:15:34

一、时间复杂度(Time Complexity)

1. 概念

时间复杂度是用来衡量算法运行时间随着输入规模增长而增长的量级。它主要关注的是算法执行基本操作的次数与输入规模之间的关系,而非具体的运行时间(因为实际运行时间会受硬件、编程语言实现等多种因素影响),旨在从理论上分析算法的效率高低。

2. 大 O 表示法(Big O Notation)

这是描述时间复杂度最常用的方式。它用一个函数来定性描述算法的运行时间与输入规模的关系。例如,对于一个排序算法,如果它的时间复杂度表示为 O(n^2),这意味着随着输入数据量(通常用n表示元素个数等输入规模相关的量)的增加,其运行时间大致按输入规模的平方量级增长。

常见的大 O 表示法的时间复杂度类型有:

  • 常数阶 O(1):无论输入规模n怎么变化,算法执行的基本操作次数是固定的,比如下面这个简单的函数:
int add(int a, int b) {
    return a + b;
}

这个函数不管传入什么样的整数参数,它都只执行了一次加法操作,基本操作次数恒定,所以时间复杂度是O(1)

  • 线性阶 :基本操作次数与输入规模  成正比。例如,遍历一个长度为  的数组并打印每个元素的操作:
#include <iostream>
void printArray(int arr[], int n) {
    for (int i = 0; i < n; ++i) {
        std::cout << arr[i] << " ";
    }
}

这里循环会执行n次,随着数组元素个数 n的增多,操作次数线性增长,时间复杂度就是O(n) 。

  • 平方阶 O(n^2):通常出现在嵌套循环中,外层循环执行n次,内层循环对于外层循环的每一次执行也执行 n 次,基本操作次数就是  n×n=n^2次。比如简单的冒泡排序算法的核心代码:
#include <iostream>
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;
            }
        }
    }
}

这里有两层嵌套循环,总的比较和交换操作次数大约是n^2量级,所以时间复杂度是O(n^2) 。

  • 对数阶O(log n) :例如在二分查找算法中,每次查找都会把搜索区间缩小一半,假设数据规模是n ,最多需要查找的次数 k满足n/2^k=1 (也就是最后只剩下一个元素还没确定是否是目标元素的时候停止查找),解这个等式可得k=log2  n ,所以二分查找的时间复杂度是O(log n) (通常省略底数 2,因为不同底数的对数之间只差一个常数系数,在大 O 表示法中可以忽略常数)。示例代码如下:
#include <iostream>
int binarySearch(int arr[], int left, int right, int target) {
    while (left <= right) {
        int mid = left + (right - left) / 2;
        if (arr[mid] == target) {
            return mid;
        } else if (arr[mid] < target) {
            left = mid + 1;
        } else {
            right = mid - 1;
        }
    }
    return -1;
}
  • 线性对数阶 O(nlog n):像快速排序、归并排序等高效的排序算法,它们的平均时间复杂度就是O(n log n) 。这些算法在每一层递归或者划分阶段基本操作次数和 n有关,而递归或者划分的层数和 log n 有关,综合起来就是O(n log n)  。以归并排序为例,代码如下:
#include <iostream>
#include <vector>

// 合并两个已排序的子数组
void merge(std::vector<int>& arr, int left, int mid, int right) {
    int n1 = mid - left + 1;
    int n2 = right - mid;

    std::vector<int> L(n1), R(n2);
    for (int i = 0; i < n1; ++i) {
        L[i] = arr[left + i];
    }
    for (int j = 0; j < n2; ++j) {
        R[j] = arr[mid + 1 + j];
    }

    int i = 0, j = 0, k = left;
    while (i < n1 && j < n2) {
        if (L[i] <= R[j]) {
            arr[k++] = L[i++];
        } else {
            arr[k++] = R[j++];
        }
    }

    while (i < n1) {
        arr[k++] = L[i++];
    }
    while (j < n2) {
        arr[k++] = R[j++];
    }
}

// 归并排序主函数
void mergeSort(std::vector<int>& arr, int left, int right) {
    if (left < right) {
        int mid = left + (right - left) / 2;
        mergeSort(arr, left, mid);
        mergeSort(arr, mid + 1, right);
        merge(arr, left, mid, right);
    }
}
3. 计算时间复杂度的步骤
  • 确定算法中的基本操作,比如赋值、比较、算术运算等操作,这些操作执行次数会随着输入规模变化而影响时间复杂度。
  • 分析基本操作执行次数与输入规模  的关系,用数学表达式表示出来,比如是 3n^2+5n+2 这样的式子。
  • 按照大 O 表示法的规则,忽略低阶项(如 5n+2 相对于 3 × n^2 在 n 足够大时影响较小)和常数系数(这里的 3),得到最终的时间复杂度表示,像上面例子就是O(n^2) 。

二、空间复杂度(Space Complexity)

1. 概念

空间复杂度衡量的是算法运行过程中所需要的额外存储空间与输入规模之间的关系。这里强调的是额外存储空间,也就是除了输入数据本身所占空间之外,算法执行过程中临时开辟的空间。

2. 同样用大 O 表示法
  • 常数阶 :算法运行过程中,不管输入规模如何变化,额外开辟的存储空间大小是固定的。例如下面这个函数,只是用了几个固定的变量,没有随输入规模增大而增大的额外空间需求:
int sum(int n) {
    int result = 0;
    for (int i = 1; i <= n; ++i) {
        result += i;
    }
    return result;
}

整个函数执行过程中,只开辟了 result 和 i 这两个固定大小的变量空间,所以空间复杂度是 O(1)。

  • 线性阶 O(n):如果算法执行过程中额外开辟的空间和输入规模 n成正比。比如创建一个长度为 n 的数组来存储数据,代码如下:
#include <iostream>
#include <vector>
std::vector<int> createArray(int n) {
    std::vector<int> arr(n);
    for (int i = 0; i < n; ++i) {
        arr[i] = i;
    }
    return arr;
}

这里创建的 vector 数组大小取决于输入的参数 n,所以空间复杂度是 O(n)。

  • 平方阶O(n^2) :例如二维数组的情况,当创建一个 n×n 的二维数组时,其元素个数就是 n^2 个,额外空间和 n^2 成正比,空间复杂度就是 O(n^2),示例代码如下:
#include <iostream>
#include <vector>
std::vector<std::vector<int>> createMatrix(int n) {
    std::vector<std::vector<int>> matrix(n, std::vector<int>(n));
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            matrix[i][j] = i * j;
        }
    }
    return matrix;
}
3. 计算空间复杂度的要点
  • 找出算法中随着输入规模变化而动态分配的额外存储空间,像动态申请的数组、递归调用栈等占用的空间。
  • 分析这些额外空间大小与输入规模 n 的数量关系,用大 O 表示法来表示,同样忽略低阶项和常数系数等,得到最终的空间复杂度描述。

总之,在实际的 C++ 编程以及算法设计中,时间复杂度和空间复杂度是衡量算法优劣的重要指标,需要根据具体的应用场景(如对时间要求高还是对空间要求高)来选择合适的算法和数据结构,平衡两者之间的关系。

 

 

 

 

 

 

 

 

 

 

 

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

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

相关文章

【Linux】【Shell】Shell 基础与变量

Shell 基础 Shell 基础查看可用的 Shell判断当前 Shell 类型 变量环境变量查看环境变量临时环境变量永久环境变量PATH 变量 自定义变量特殊赋值(双引号、单引号、反撇号) 预定义变量bashrc Shell 基础 Shell 是一个用 C 语言编写的程序&#xff0c;相当于是一个翻译&#xff0c…

【SpringBoot】26 实体映射工具(MapStruct)

Gitee 仓库 https://gitee.com/Lin_DH/system 介绍 现状 为了让应用程序的代码更易于维护&#xff0c;通常会将项目进行分层。在《阿里巴巴 Java 开发手册》中&#xff0c;推荐分层如下图所示&#xff1a; 每层都有对应的领域模型&#xff0c;即不同类型的 Bean。 DO&…

理解和选择Vue的组件风格:组合式API与选项式API详解

目录 前言1. Vue 的两种组件风格概述1.1 选项式 API&#xff1a;直观且分块清晰1.2 组合式 API&#xff1a;灵活且逻辑集中 2. 深入理解组合式 API 的特点2.1 响应式变量与函数式编程2.2 逻辑组织更清晰2.3 更好的代码复用 3. 应用场景分析&#xff1a;如何选择 API 风格3.1 适…

Windows和mac OS共用VMware虚拟机

在Windows下使用VMware Workstation Pro创建的虚拟机&#xff0c;是以文件夹形式存储在硬盘中的&#xff0c;在mac OS中对应的虚拟机产品是VMware Fusion&#xff0c;那么在Windows下创建的虚拟机怎么在mac OS中使用呢&#xff1f; 在下图中我们可以看到&#xff0c;Windows 1…

【K8S系列】Kubernetes Pod节点ImagePullBackOff 状态及解决方案详解【已解决】

在 Kubernetes 中,当某个 Pod 的容器无法从指定的镜像仓库拉取镜像时,Pod 的状态会变为 ImagePullBackOff。这通常是因为指定的镜像不存在、镜像标签错误、认证失败或网络问题等原因。 以下是关于 ImagePullBackOff 的详细分析及解决方案。 1. ImagePullBackOff 状态分析 1.…

04 —— Webpack打包CSS代码

加载器css-loader &#xff1a;解析css代码 webpack 中文文档 | webpack中文文档 | webpack中文网 加载器style-loader&#xff1a;把解析后的css代码插入到DOM style-loader | webpack 中文文档 | webpack中文文档 | webpack中文网 准备css代码&#xff0c;放到src/login目…

单元测试框架gtest学习(二)—— 认识断言

前言 我们在上一篇文章中&#xff0c;简要介绍了gtest是如何使用的 单元测试框架gtest学习&#xff08;一&#xff09;——初始gtest-CSDN博客 这篇文章我们主要总结gtest中的所有断言相关的宏。首先需要明确的是&#xff0c;gtest是通过各种断言宏进行测试的&#xff0c;而这…

网安瞭望台第2期:零日漏洞密集爆发、2024年常见网络安全漏洞类型及分析

国内外要闻 Ubuntu 服务器 Needrestart 软件包惊现严重安全漏洞 近日&#xff0c;Ubuntu 服务器&#xff08;自 21.04 版本起默认安装&#xff09;的 Needrestart 软件包被曝存在多个可追溯至数十年前的安全漏洞。这些漏洞允许本地攻击者在无需用户交互的情况下获取根…

PDF内容提取,MinerU使用

准备环境 # python 3.10 python3 -m pip install huggingface_hub python3 -m pip install modelscope python3 -m pip install -U magic-pdf[full] --extra-index-url https://wheels.myhloli.com下载需要的模型 import json import osimport requests from huggingface_hub…

【TDOA最小二乘解算】两步最小二乘迭代的TDOA解算方法,适用于二维平面、自适应锚点(附MATLAB代码)

本文所述的MATLAB代码实现了一个基于两步加权最小二乘法的二维目标定位算法&#xff0c;利用多个锚点&#xff08;基站&#xff09;和时间差到达&#xff08;TDOA&#xff09;数据来估计未知目标的位置。 文章目录 运行结果代码代码功能概述代码结构和详细说明初始化部分参数和…

Comfy UI 工作流(三)高清修复 - 低显存放大

前面提到的几种高清修复、高清放大都会占用&#xff0c;较多的现存。 和 Sd Web UI 中的 SD Upscale 放大一样&#xff0c;Comfy UI 中也提供有类似的自定义节点&#xff0c;通过分块的方式进行图片放大&#xff0c;从而减少现、显存的占用&#xff0c;该方式也经常用于 Com…

形态学图像处理(Morphological Image Processing)

形态学图像处理(Morphological Image Processing) 前言 ‍ 本博客为个人总结数字图像处理一课所写&#xff0c;并给出适当的扩展和相应的demo。 写博客跟做 checkpoint​ 很像&#xff0c;毕竟个人还不能达到那种信手拈来的境界&#xff0c;忘了就是从零开始训练&#xff0…

数据库迁移--laravel进阶篇

本地开发中的数据库和线上发布的数据库是不一样的,每进行一个线上版本的更新,很可能也涉及大量数据库的改动,那么这些数据库的改动在laravel中可以使用数据库迁移来处理。 比如我想创建一张flights数据表 执行php artisan make:migration create_flights_table命令就能自动生…

正则表达式完全指南,总结全面通俗易懂

目录 元字符 连接符 限定符 定位符 修饰符&#xff08;标记&#xff09; 运算符优先级 普通字符集及其替换 零宽断言 正向先行断言 负向先行断言 正向后发断言 负向后发断言 捕获组 普通捕获组 命名捕获组 PS:非捕获组 正则表达式在线测试: 正则在线测试工具 …

vulfocus在线靶场:CVE-2018-7600 速通手册

目录 一、启动环境&#xff0c;访问页面&#xff0c;语言选择中文&#xff0c;打开phpmyadmin 二、phpmyadmin中打开小房子 三、选择显示php信息 四、ctrlF&#xff0c;搜索flag&#xff0c;复制粘贴到任务中&#xff0c;通关 一、启动环境&#xff0c;访问页面&#xff0c;…

springboot实战(13)(@PatchMapping、@RequestParam、@URL、ThreadLocal线程局部变量)

目录 一、PATCH请求方式。 二、实现用户更新头像功能。 三、注解RequestParam。 四、注解URL。&#xff08;对传来的参数是否是合法地址进行校验&#xff09; 一、PATCH请求方式。 patch中文翻译&#xff1a;局部、小块。PATCH 请求主要用于对已存在的资源进行局部修改&#xf…

记录下,用油猴Tampermonkey监听所有请求,绕过seesion

油猴Tampermonkey监听所有请求&#xff0c;绕过seesion 前因后果脚本编写 前因后果 原因是要白嫖一个网站的接口&#xff0c;这个接口的页面入口被隐藏掉了&#xff0c;不能通过页面调用&#xff0c;幸好之前有想过逆向破解通过账号密码模拟登录后拿到token&#xff0c;请求该…

Spring Cloud Hystrix 豪猪哥 服务容错与保护组件

Spring Cloud Hystrix 豪猪哥 服务容错与保护组件 一、Spring Cloud Hystrix概述1.简介2.核心功能3.雪崩效应 二、Hystrix服务降级1.服务降级简介2.样例准备3.服务提供者降级4.服务消费者降级5.全局降级方法6.对API接口降级方法 三、Hystrix服务熔断1.服务熔断机制简介2.熔断工…

Facebook商城号封号的原因是什么?

Facebook商城作为一个重要的销售平台&#xff0c;不仅为商家提供了巨大的市场机会&#xff0c;也带来了一系列需要警惕的风险&#xff0c;其中包括账号被封的风险。本文将从环境异常、频繁操作和违规行为三个主要方面深入探讨&#xff0c;解析导致Facebook商城账号被封禁的具体…

Android okhttp 网络链接各阶段监控

步骤 1: 添加依赖 在项目的 build.gradle 文件中&#xff0c;添加 OkHttp 依赖&#xff1a; implementation com.squareup.okhttp3:okhttp:4.11.0 步骤 2: 创建自定义的 EventListener 创建一个自定义的 EventListener 类&#xff1a; import android.util.Log import okht…