时间复杂度的相关概念

1. 统计时间增长趋势

时间复杂度分析统计的不是算法运行时间,而是算法运行时间随着数据量变大时的增长趋势,也就是算法运行时间与输入数据的关系。

// 算法 A 的时间复杂度:常数阶
function algorithm_A(n) {
  console.log(0);
}
// 算法 B 的时间复杂度:线性阶
function algorithm_B(n) {
  for (let i = 0; i < n; i++) {
    console.log(0);
  }
}
// 算法 C 的时间复杂度:常数阶
function algorithm_C(n) {
  for (let i = 0; i < 1000000; i++) {
    console.log(0);
  }
}

● 算法 A 只有 1 个打印操作,算法运行时间不随着 n 增大而增长。我们称此算法的时间复杂度为“常数阶”。
● 算法 B 中的打印操作需要循环 n 次,算法运行时间随着 n 增大呈线性增长。此算法的时间复杂度被称为“线性阶”。
● 算法 C 中的打印操作需要循环 1000000 次,虽然运行时间很长,但它与输入数据大小 n 无关。因此 C 的时间复杂度和 A 相同,仍为“常数阶”。
在这里插入图片描述

2. 函数渐近上界

function algorithm(n) {
    var a = 1; // +1
    a += 1; // +1
    a *= 2; // +1
    // 循环 n 次
    for(let i = 0; i < n; i++){ // +1(每轮都执行 i ++)
        console.log(0); // +1
    }
}

设算法的操作数量是一个关于输入数据大小 n 的函数,记为 T ( n ) T(n) T(n) ,则以上函数的操作数量为: T ( n ) = 3 + 2 n T(n) = 3+2n T(n)=3+2n
T ( n ) T(n) T(n)是一次函数,说明其运行时间的增长趋势是线性的,因此它的时间复杂度是线性阶。
我们将线性阶的时间复杂度记为 O ( n ) O(n) O(n) ,这个数学符号称为大 O 记号(big-O notation),表示函数 T ( n ) T(n) T(n)渐近上界(asymptotic upper bound)。
在这里插入图片描述

所以要知道执行的时间复杂度,就是要确定 f ( n ) f(n) f(n)

3. 推算方法

步骤1: 统计操作数量 T(n)

由于上述 c⋅f(n) 中的常数项 c 可以取任意大小,因此操作数量 T(n) 中的各种系数、常数项都可以忽略,循环嵌套时使用乘法。

function algorithm(n) {
    let a = 1;  // +0(技巧 1)
    a = a + n;  // +0(技巧 1)
    // +n(技巧 2)
    for (let i = 0; i < 5 * n + 1; i++) {
        console.log(0);
    }
    // +n*n(技巧 3)
    for (let i = 0; i < 2 * n; i++) {
        for (let j = 0; j < n + 1; j++) {
            console.log(0);
        }
    }
}

所以简化后的操作数量 T ( n ) = n 2 + n T(n) = n^2+n T(n)=n2+n

步骤2:判断渐近上界

时间复杂度由 T ( n ) T(n) T(n)最高阶的项来决定.
在这里插入图片描述

所以上面例子的时间复杂度为 O ( n 2 ) O(n^2) O(n2)

4. 常见的时间复杂度类型

在这里插入图片描述

在这里插入图片描述

常数阶 O(1)

操作数量与输入数据大小n 无关

/* 常数阶 */
function constant(n) {
    let count = 0;
    const size = 100000;
    for (let i = 0; i < size; i++) count++;
    return count;
}

线性阶O(n)

操作数量取决于输入数据大小n,随着n变化

/* 线性阶 */
function linear(n) {
    let count = 0;
    for (let i = 0; i < n; i++) count++;
    return count;
}

平方阶

通常出现在内外循环中

/* 平方阶 */
function quadratic(n) {
    let count = 0;
    // 循环次数与数据大小 n 成平方关系
    for (let i = 0; i < n; i++) {
        for (let j = 0; j < n; j++) {
            count++;
        }
    }
    return count;
}

指数阶

/* 指数阶(循环实现) */
function exponential(n) {
    let count = 0,
        base = 1;
    // 细胞每轮一分为二,形成数列 1, 2, 4, 8, ..., 2^(n-1)
    for (let i = 0; i < n; i++) {
        for (let j = 0; j < base; j++) {
            count++;
        }
        base *= 2;
    }
    // count = 1 + 2 + 4 + 8 + .. + 2^(n-1) = 2^n - 1 = z^n
    return count;
}

对数阶

一般用于分治策略中,比如二分法。

与指数阶相反,对数阶反映了“每轮缩减到一半”的情况。设输入数据大小为 n ,由于每轮缩减到一半,因此循环次数是 l o g 2 n log_2^n log2n ,即 2 n 2^n 2n 的反函数。

/* 对数阶(循环实现) */
function logarithmic(n) {
    let count = 0;
    while (n > 1) {
        n = n / 2;
        count++;
    }
    return count;
}

T ( n ) = l o g 2 n + 1 T(n) = log_2^n+1 T(n)=log2n+1
O ( n ) = l o g 2 n O(n) = log_2^n O(n)=log2n

在这里插入图片描述

所以无论底数m为多少,对其本身的时间复杂度不影响,所以通常会省略底数m。

线性对数阶

线性对数阶常出现于嵌套循环中,两层循环的时间复杂度分别为 O ( l o g n ) O(log^n) O(logn) O ( n ) O(n) O(n)

/* 线性对数阶 */
function linearLogRecur(n) {
    if (n <= 1) return 1;
    let count = linearLogRecur(n / 2) + linearLogRecur(n / 2);
    for (let i = 0; i < n; i++) {
        count++;
    }
    return count;
}

阶乘阶 O(n!)

阶乘阶对应数学上的“全排列”问题。给定 n 个互不重复的元素,求其所有可能的排列方案,方案数量为:
在这里插入图片描述

阶乘通常使用递归实现。

/* 阶乘阶(递归实现) */
function factorialRecur(n) {
    if (n === 0) return 1;
    let count = 0;
    // 从 1 个分裂出 n 个
    for (let i = 0; i < n; i++) {
        count += factorialRecur(n - 1);
    }
    return count;
}

5. 最差、最佳、平均时间复杂度

假设输入一个长度为 n 的数组 nums ,其中 nums 由从 1 至 n 的数字组成,每个数字只出现一次;但元素顺序是随机打乱的,任务目标是返回元素 1 的索引。我们可以得出以下结论。
● 当 nums = [?, ?, …, 1] ,即当末尾元素是 1 时,需要完整遍历数组,达到最差时间复杂度 O ( n ) O(n) O(n)
● 当 nums = [1, ?, ?, …] ,即当首个元素为 1 时,无论数组多长都不需要继续遍历,达到最佳时间复杂度 O ( 1 ) O(1) O(1)

“最差时间复杂度”对应函数渐近上界,使用大 O O O记号表示。相应地,“最佳时间复杂度”对应函数渐近下界,用 Ω Ω Ω记号表示

比如上述示例,由于输入数组是被打乱的,因此元素 1 出现在任意索引的概率都是相等的,那么算法的平均循环次数就是数组长度的一半 n/2 ,平均时间复杂度为 Θ(n/2)=Θ(n) 。
在这里插入图片描述


🔍空间复杂度的相关概念
参考:https://www.hello-algo.com/

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

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

相关文章

二叉树(数据结构篇)

数据结构之二叉树 二叉树 概念&#xff1a; 二叉树(binary tree)是一颗每个节点都不能多于两个子节点的树&#xff0c;左边的子树称为左子树&#xff0c;右边的子树称为右子树 性质&#xff1a; 二叉树实际上是图&#xff0c;二叉树相对于树更常用。 平衡二叉树的深度要比…

(3) cmake编译多个cpp文件

文章目录 概要整体代码运行结果 概要 上一节中实现了对单个cpp文件用cmake编译。这一节升级一下 整体代码 main.cpp #include <iostream> #include "person.h"using namespace std;int main() {person me person("langdaoliu", 28, "engin…

nuc算法设计与分析 ppt总结

总纲 插入排序算法 内容&#xff1a; 将数组待排序的元素依次插入到已排序部分&#xff0c;使已排序部分保持升序的性质。 伪代码&#xff1a; 复杂度分析&#xff1a; 时间复杂度为O(n^2)&#xff0c;空间复杂度为O(1)。在数据量较小的情况下&#xff0c;插入排序的效率不输给…

Linux服务器挖矿病毒处理

文章目录 Linux服务器挖矿病毒处理1.中毒表现2.解决办法2.1 断网并修改root密码2.2 找出隐藏的挖矿进程2.3 关闭病毒启动服务2.4 杀掉挖矿进程 3. 防止黑客再次入侵3.1 查找异常IP3.2 封禁异常IP3.3 查看是否有陌生公钥 补充知识参考 Linux服务器挖矿病毒处理 情况说明&#x…

echarts dataZoom用按钮代替鼠标滚轮实现同样效果

2024.06.19今天我学习了echarts dataZoom如何用按钮来控制放大缩小的功能&#xff0c; 效果如下&#xff1a; 通过控制按钮来实现图表放大缩小数据的效果。 步骤如下&#xff1a; 一、写缩放按钮&#xff0c;以及图表数据。 二、设置初始位置的变量&#xff0c;我这边是七个…

InPixio Photo Cutter v10 解锁版安装教程 (懒人抠图工具)

前言 InPixio Photo Cutter是一款懒人抠图工具&#xff0c;采用了增强的算法切割技术&#xff0c;可以在不影响图像质量的情况下&#xff0c;允许用户从照片中删除任何物体或人物&#xff0c;并且保持其完整的质量。你只需点击几下鼠标&#xff0c;便可从照片中剪下任何细节、…

上位机图像处理和嵌入式模块部署(h750 mcu中的pwm控制)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 所谓的pwm&#xff0c;其实就是方波。我们都知道&#xff0c;对于一个电机来说&#xff0c;如果插上正负极的话&#xff0c;那么电机就会全速运转。…

C#.Net筑基-集合知识全解

01、集合基础知识 .Net 中提供了一系列的管理对象集合的类型&#xff0c;数组、可变列表、字典等。从类型安全上集合分为两类&#xff0c;泛型集合 和 非泛型集合&#xff0c;传统的非泛型集合存储为Object&#xff0c;需要类型转。而泛型集合提供了更好的性能、编译时类型安全…

spring cloud Alibaba 整合 seata AT模式

准备工作&#xff1a; 1、MySQL正常安装并启动 2、nacos正常部署并启动 3、下载 Seata-1.4.2 源码包和 seata-server-1.4.2 服务端源码包&#xff08;版本根据自己的需要选择&#xff0c;我这里选择1.4.2&#xff09; 下载地址&#xff1a; Seata&#xff1a;https://gite…

PFA托盘400*300*42mm耐酸碱透明聚四氟乙烯方盘方槽耐高温厂家供

PFA方盘又称托盘&#xff1a;耐高温、耐腐蚀。 进口透明可溶性聚四氟乙烯方盘。可应用于成膜实验&#xff0c;样品液体脱漏等。能放在电热板上直接加热使用&#xff0c;也可以用于烘箱烘干&#xff0c;实验室腐蚀性样品的转移和搬运&#xff0c;防止腐蚀性液体洒落。 产品特性…

计算机网络 —— 应用层(FTP)

计算机网络 —— 应用层&#xff08;FTP&#xff09; FTP核心特性&#xff1a;运作流程&#xff1a; FTP工作原理主动模式被动模式 我门今天来看应用层的FTP&#xff08;文件传输协议&#xff09; FTP FTP&#xff08;File Transfer Protocol&#xff0c;文件传输协议&#x…

sprintboot依赖管理和自动配置

springboot依赖管理和自动配置 依赖管理和自动配置依赖管理什么是依赖管理修改自动仲裁/默认版本号 starter场景启动器starter场景启动器基本介绍官方提供的starter第三方starter 自动配置自动配置基本介绍SpringBoot自动配置了哪些?如何修改默认配置如何修改默认扫描包结构re…

基于SSM的足球联赛管理系统

文章目录 项目介绍主要功能截图&#xff1a;部分代码展示设计总结项目获取方式 &#x1f345; 作者主页&#xff1a;超级无敌暴龙战士塔塔开 &#x1f345; 简介&#xff1a;Java领域优质创作者&#x1f3c6;、 简历模板、学习资料、面试题库【关注我&#xff0c;都给你】 &…

南开大学漏洞报送证书【文尾有福利】

证书介绍 获取来源&#xff1a;edusrc&#xff08;教育漏洞报告平台&#xff09; url&#xff1a;教育漏洞报告平台(EDUSRC) 兑换价格&#xff1a;30金币​ 获取条件&#xff1a;南开大学任意中危或以上级别漏洞 证书规格&#xff1a;证书做了木框装裱&#xff0c;显得很高…

查看电脑支持的CUDA安装版本与显卡驱动更新

说明&#xff1a; torch版本依赖于CUDA版本与Python版本 Start Locally | PyTorchCUDA版本依赖于显卡驱动版本 1. CUDA 12.5 Release Notes — Release Notes 12.5 documentation 显卡驱动版本依赖于显卡型号与电脑系统 当前电脑3060显卡&#xff0c;安装了CUDA V11.6与torc…

python-画正方形

[题目描述] 输入一个正整数n&#xff0c;要求输出一个n行n列的正方形图案&#xff08;参考样例输入输出&#xff09;。图案由大写字母组成。 其中&#xff0c;第1行以大写字母A开头&#xff0c;第2行以大写字母B开头&#xff0c;以此类推&#xff1b;在每行中&#xff0c;第2列…

使用ASM动态创建接口实现类

使用ASM动态生成一个接口的实现类&#xff0c;接口如下&#xff1a; public interface ISayHello {public void MethodA();public void MethodB();public void Abs(); } 具体实现如下&#xff1a; public class InterfaceHandler extends ClassLoader implements Opcodes {pu…

DV、OV通配符SSL证书有什么区别

通配符SSL证书是经常提及的一种SSL证书类型&#xff0c;也被称为泛域名SSL证书。通配符证书在SSL证书当中是比较特殊的&#xff0c;它具有保护主域名及其下一级所有子域名的功能&#xff0c;非常适合子域名多的域名网站&#xff0c;能够有效的节省成本&#xff0c;并降低证书管…

iis下asp.netcore后台定时任务会取消

问题 使用BackgroundService或者IHostedService做后台定时任务的时候部署到iis会出现不定时定时任务取消的问题&#xff0c;原因是iis会定时的关闭网站 解决 应用程序池修改为AlwaysRunning 修改web.config <?xml version"1.0" encoding"utf-8"?…

研究Redis源码的一些前期准备

一 背景 Redis数据结构讲完后&#xff0c;觉得还是有点不过瘾&#xff0c;想研究一下Redis的底层实现。找了一些相关资料&#xff0c;准备借鉴和学习其他各位大佬钻研Redis底层的方法和经验&#xff0c;掌握Redis实现的基本原理。 二 源码归类 网上有大佬已经总结了…