时间复杂度详解2——时间复杂度的计算

news2024/11/23 15:44:00

时间复杂度基本计算规则:

  • 基本操作即只有常数项,认为其时间复杂度为O(1)
  • 顺序结构,时间复杂度按加法进行计算
  • 循环结构,时间复杂度按乘法进行计算
  • 分支结构,时间复杂度取最大值
  • 判断一个算法效率时,往往只需要关注操作数量的最高次项,其他次要项和常数项可以忽略
  • 在没有特殊说明时,我们所分析的时间复杂度都是指最坏时间复杂度

单层循环时间复杂度计算

例题分析

 例一:

i = n*n;
whlie(i != 1)
    i = i/2;

我们发现,循环执行的条件是i!=1,然后循环体中i=i/2;改变了i的值。

我们列出循环体执行次数t  和i的最终值(即执行完t次循环后的值)的关系

循环体执行次数t0123
i的改变量in^{2}\frac{n^{2}}{2}\frac{n^{2}}{4}\frac{n^{2}}{8}

           
第二步:找到t的最终值与i的关系:

i=\frac{n^{2}}{2^{t}}

第三步:确定循环停止条件:

i=1

第四步:联立第二步第三步两式解方程:

\frac{n^{2}}{2^{t}}=1     n^{2}=2^{t} 

两边对2取对数得    t=log_{2}n^{2}=2log_{2}n

所以得到时间复杂度为:T=O(log_{2}n)

例二:

x = 0;
while (n>=(x+1)*(x+1))
    x = x+1;

第一步:列出循环趟数t及x的最终值(即执行完t次循环后的值)

循环体执行次数t0123
x的最终值x0123


第二步:找到t与x的最终值关系:

t=x

第三步:确定循环停止条件:

n=(x+1)^{2}

第四步:联立第二步第三步两式解方程:

n=(t+1)^{2}

t=\sqrt{n}-1

所以得到时间复杂度为:

T=O(\sqrt{n})

 例三:

int i = 1;
while (i<=n)
    i = i *2

第一步:列出循环趟数t及i的最终值(即执行完t次循环后的值)

循环趟数t0123
i的最终值1248


第二步:找到t与i的关系:

i=2^{t}

第三步:确定循环停止条件:

i=n

第四步:联立第二步第三步两式解方程:

n=2^{t}

t=log_{2}n

所以得到时间复杂度为:

T=O(log_{2}n)

 例四:

int i = 0;
while (i*i*i<=n)
    i ++;


第一步:列出循环趟数t及i的最终值:

循环趟数t0123
i的最终值i0123


第二步:找到t与i的关系:

t=i

第三步:确定循环停止条件:

i^{3}=n

第四步:联立第二步第三步两式解方程:

t^{3}=n

t=\sqrt[3]{n}

所以得到时间复杂度为:

T=O(\sqrt[3]{n})

 例五:

y = 0;
while (y+1)*(y+1) <= n)
    y = y+1;


第一步:列出循环趟数t及y的最终值:

循环趟数t0123
y的最终值0123


第二步:找到t与y的关系:

t=y

第三步:确定循环停止条件:

(y+1)^{2}=n

第四步:联立第二步第三步两式解方程:

(t+1)^{2}=n

t=\sqrt{n}-1

所以得到时间复杂度为:

T=O(\sqrt{n})

两层循环时间复杂度计算

对于两层循环时间复杂度的计算,我们完全可以将这个循环视作一个单层循环,然后它的循环体是另一个循环。这样子计算起时间复杂度就很简单了

例题分析

例一:

int m=0,i,j;
for (i=1;i<=n;i++)
    for(j=1;j<=2*i;j++)
        m++;


第一步列出是第n次大循环

第二步列出在第n次大循环中内循环层语句能执行的次数:

第几次大循环123……n
第n次大循环中内循环语句的执行次数246……2n

注意上面这个都是每次大循环对应的内部循环语句执行次数,整个语句的时间复杂度还需要把每次大循环中内循环语句执行的次数相加起来

注意是将内循环语句次数相加

第三步 求和,写结果

2+4+...+2n=n(n+1)

T=O(n^{2})

 例二:

for (i=0;i<n;i++)
    for(j=0;j<m;j++)
        a[i][j] = 0;

第一步列出第n次大循环:

第二步列出第n次大循环中内层语句的执行次数:

第n次大循环123...n
第n次大循环中内层循环语句的执行次数mmmmm

注意上面这个都是每次大循环对应的内部循环语句执行次数,整个语句的时间复杂度还需要把每次大循环中内循环语句执行的次数相加起来


第三步 求和,写结果

注意是将内循环语句次数相加

T=O(m*n)

 例三:

count = 0;
for (k=1;k<=n;k*=2)
    for(j=1;j<=n;j++)
        count ++;

第一步列出第a次大循环:(注意这里k*=2)

第二步列出第a次大循环中内层语句的执行次数:

由于这里大循环能执行的次数不是一眼就能看出来的,所以还得先计算能执行多少次大循环

假设能执行t次大循环,则

k=2^{t-1}

k=n

n=2^{t-1}

t=log_{2}n+1

第a次大循环123……log_{2}n+1
k的值124……n
第a次大循环中内层语句的执行次数nnnnn

内层每个都是n,求和则可以得到:

注意是将内循环语句次数相加

内循环语句总的执行次数是

(log_{2}n+1)n

T=O(nlog_{2}n)

 例四:

for (i=n-1;i>=1;i--)
    for(j=1;j<=i;j++)
        if (A[j] > A [j+1])
            {//时间复杂度为O(1)的语句
}

第一步列出第t次大循环:

第二步列出第t次大循环中内层语句的执行次数:

第t次大循环123...n-1
第t次大循环中内层循环语句的执行次数n-1n-2n-3...1

第三步 求和,写结果

注意是将内循环语句次数相加

在本例中它就是个等差数列求和

(n-1) + (n-2) + \ldots + 2 + 1 = \frac{(n-1) \times n}{2}

所以时间复杂度就是

T=O(n^{2})

多层循环时间复杂度计算

实际上无论多少层循环,我们只需一层一层分开来计算就行了,最后只需将最内层的语句的执行次数全加起来就好了

例一:

for(i=0;i<=n;i++)
    for(j=0;j<=i;j++)
        for(k=0;k<j;k++)

要计算这个嵌套循环的时间复杂度,我们首先要分析每个循环的执行次数。

外层循环 for(i=0;i<=n;i++) 从 0 到 n,因此会执行 n+1 次。

对于每一个 i 的值,内层循环 for(j=0;j<=i;j++) 的执行次数从 0 到 i。具体来说:

  • 当 i = 0 时,内层循环执行 1 次(j 从 0 到 0)。
  • 当 i = 1 时,内层循环执行 2 次(j 从 0 到 1)。
  • 当 i = 2 时,内层循环执行 3 次(j 从 0 到 2)。
  • ...
  • 当 i = n 时,内层循环执行 n+1 次(j 从 0 到 n)。

因此,内层循环的总执行次数是 1 + 2 + 3 + ... + (n+1),这是一个等差数列的和,其和为 (\frac{(n+1)(n+2)}{2})。

对于每一个 j 的值,最内层的循环 for(k=0;k<j;k++) 的执行次数是从 0 到 j-1。这实际上是一个等差数列的前 j 项和,但我们要注意,这个内层循环对于每一个 j 都会执行,所以我们需要将它与 j 的所有可能值相乘。

具体来说,当 j 分别取 0, 1, 2, ..., n 时,最内层循环的执行次数分别是 0, 1, 3, ..., n(n-1)/2。因此,最内层循环的总执行次数是这些值的和。

为了计算这个总和,我们可以观察到一个事实:每一个 k 值在 j 从 k+1 到 n 的过程中都会被计算一次。因此,k=0 会被计算 n 次,k=1 会被计算 n-1 次,以此类推,直到 k=n-1 只被计算 1 次。

这个总和是另一个等差数列的和,其和为 \frac{n(n-1)}{2}

因此,整个嵌套循环的时间复杂度是外层循环次数 n+1 乘以最内层循环的总执行次数 (\frac{n(n-1)}{2}),即(n+1) \times \frac{n(n-1)}{2}

简化后得到时间复杂度为 (O(n^3))。这是因为尽管有系数和较低阶的项,但在大 n 的情况下,(n^3) 项将主导整个表达式,因此时间复杂度是立方级的。

常见时间复杂度计算举例

实例1:

// 计算Func2的时间复杂度?
 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); 
}

 实例1基本操作执行了2N+10次,通过推导大O阶方法知道,时间复杂度为 O(N) 

实例2

// 计算Func3的时间复杂度?
 void Func3(int N, int M)
 { int count = 0; 
for (int k = 0; k < M; ++ k) 
{ ++count; } 

for (int k = 0; k < N ; ++ k) 
{ ++count; } 

printf("%d\n", count);
 }

实例2基本操作执行了M+N次,有两个未知数M和N,时间复杂度为 O(N+M) 

实例3

// 计算Func4的时间复杂度? 
void Func4(int N)
 { int count = 0; 
for (int k = 0; k < 100; ++ k) 
{ ++count; } 
printf("%d\n", count);
 }

 实例3基本操作执行了10次,通过推导大O阶方法,时间复杂度为 O(1)

实例4

// 计算strchr的时间复杂度?

const char * strchr ( const char * str, int character );

实例4基本操作执行最好1次,最坏N次,时间复杂度一般看最坏,时间复杂度为 O(N)

实例5 

// 计算BubbleSort的时间复杂度? 
void BubbleSort(int* a, int n) 
{ assert(a);

 for (size_t end = n; end > 0; --end)
 { int exchange = 0; 

for (size_t i = 1; i < end; ++i) 
{ 
if (a[i-1] > a[i]) 

{ Swap(&a[i-1], &a[i]); 
exchange = 1; 
} 

} 
if (exchange == 0)
 break;
} 
} 

实例5基本操作执行最好N次,最坏执行了(N*(N+1)/2次,通过推导大O阶方法+时间复杂度一般看最坏,时间复杂度为 O(N^2)

实例6 

// 计算BinarySearch的时间复杂度?
 int BinarySearch(int* a, int n, int x) 
{ 
assert(a);
 
int begin = 0; 
int end = n-1; 
while (begin < end)
 { int mid = begin + ((end-begin)>>1); 

if (a[mid] < x) 

begin = mid+1;

 else if (a[mid] > x) 

end = mid; 

else return mid;

 }
 return -1;
 }

实例6基本操作执行最好1次,最坏O(logN)次,时间复杂度为 O(logN).。ps:logN在算法分析中表示是底数为2,对数为N。有些地方会写成lgN。(建议通过折纸查找的方式讲解logN是怎么计算出来的)

实例7

// 计算阶乘递归Fac的时间复杂度?

long long Fac(size_t N) 
{ if(0 == N) 

return 1;

 return Fac(N-1)*N; }

实例7通过计算分析发现基本操作递归了N次,时间复杂度为O(N) 

实例8

// 计算斐波那契递归Fib的时间复杂度?

 long long Fib(size_t N)

 { 
if(N < 3) 
return 1; 

return Fib(N-1) + Fib(N-2); }

实例8通过计算分析发现基本操作递归了2^N次,时间复杂度为O(2^N)。(建议画图递归栈帧的二叉树 讲解)

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

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

相关文章

HubSpot如何通过自动化和优化客户服务流程?

在当今竞争激烈的市场环境中&#xff0c;提供卓越的客户服务体验已经成为企业赢得客户忠诚、推动业务增长的关键所在。HubSpot&#xff0c;作为一款领先的客户关系管理软件&#xff0c;通过自动化和优化客户服务流程&#xff0c;为企业带来了革命性的服务体验提升。 HubSpot通…

对LSTM的通俗易懂理解--可变权重

RNN的问题&#xff1a;长期依赖&#xff0c;即对短期的数据敏感&#xff0c;对比较远的长期数据不敏感&#xff0c;这是因为RNN隐藏状态权重在不同时刻是共享相同的&#xff0c;随着时间步的增加&#xff0c;梯度会指数级地衰减或者增长&#xff0c;导致梯度消失或者爆炸&#…

稀碎从零算法笔记Day47-LeetCode:找到冠军 I

或许是昨天的每日一题太难了&#xff0c;今天的简单 题型&#xff1a;数组、矩阵 链接&#xff1a;2923. 找到冠军 I - 力扣&#xff08;LeetCode&#xff09; 来源&#xff1a;LeetCode 题目描述 一场比赛中共有 n 支队伍&#xff0c;按从 0 到 n - 1 编号。 给你一个下…

华为海思校园招聘-芯片-数字 IC 方向 题目分享——第三套

华为海思校园招聘-芯片-数字 IC 方向 题目分享——第三套 (共9套&#xff0c;有答案和解析&#xff0c;答案非官方&#xff0c;未仔细校正&#xff0c;仅供参考&#xff09; 部分题目分享&#xff0c;完整版获取&#xff08;WX:didadidadidida313&#xff0c;加我备注&#x…

大数据架构之关系型数据仓库——解读大数据架构(二)

文章目录 前言什么是关系型数仓对数仓的错误认识与使用自上而下的方法关系型数仓的优点关系型数仓的缺点数据加载加载数据的频率如何确定变更数据 关系型数仓会消失吗总结 前言 本文对关系型数据仓库&#xff08;RDW&#xff09;进行了简要的介绍说明&#xff0c;包括什么是关…

《由浅入深学习SAP财务》:第2章 总账模块 - 2.6 定期处理 - 2.6.5 年末操作:维护新财政年度会计凭证编号范围

2.6.5 年末操作&#xff1a;维护新财政年度会计凭证编号范围 财务系统的维护者要在每年年末预先设置好下一年度的会计凭证编号范围&#xff08;number range&#xff09;&#xff0c;以便下一年度会计凭证能够顺利生成。这一操作一定要在下一年度1月1日以前预先完成。 …

C#/.NET/.NET Core拾遗补漏合集(24年4月更新)

前言 在这个快速发展的技术世界中&#xff0c;时常会有一些重要的知识点、信息或细节被忽略或遗漏。《C#/.NET/.NET Core拾遗补漏》专栏我们将探讨一些可能被忽略或遗漏的重要知识点、信息或细节&#xff0c;以帮助大家更全面地了解这些技术栈的特性和发展方向。 GitHub开源地…

【MoS2】应变增强的单层MoS2光电探测器

这篇文章的标题是《Strain-Enhanced Large-Area Monolayer MoS2 Photodetectors》&#xff0c;作者是Borna Radatovic等人&#xff0c;发表在《ACS Applied Materials & Interfaces》期刊的2024年第16卷。文章主要研究了应变增强的大面积单层MoS2光电探测器的性能和应用潜力…

基于SpringBoot的“汉服文化平台网站”的设计与实现(源码+数据库+文档+PPT)

基于SpringBoot的“汉服文化平台网站”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBoot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统功能结构图 系统功能界面图 用户登录、用…

Java并发(1)--线程,进程,以及缓存

线程和进程是什么&#xff1f; 进程 进程是程序的一次执行过程&#xff0c;系统程序的基本单位。有自己的main方法&#xff0c;并且主要由主方法运行起来的基本上就是进程。 线程 线程与进程相似&#xff0c;但线程是一个比进程更小的执行单位。一个进程在其执行的过程中可以…

ARM64架构栈帧回溯

文章目录 前言一、栈帧简介二、demo演示 前言 请参考&#xff1a;ARM64架构栈帧以及帧指针FP 一、栈帧简介 假设下列函数调用&#xff1a; funb() {func() }funa() {funb() }main() {funa() }main函数&#xff0c;funa函数&#xff0c;funb函数都不是叶子函数&#xff0c;其…

PostgreSQL数据库基础--简易版

数据库 其中runoobdb为数据库名 查看已经存在的数据库 \l进入数据库 \c runoobdb创建数据库 CREATE DATABASE runoobdb;删除数据库 DROP DATABASE runoobdb;表 其中COMPANY为表名 创建表格 CREATE TABLE COMPANY(ID INT PRIMARY KEY NOT NULL,NAME TEXT…

lua学习笔记21完结篇(lua中的垃圾回收)

print("*****************************lua中的垃圾回收*******************************") text{id24,name"仙贝"} --垃圾回收关键字collectgarbag --获取当前lua占用内存数 k字节 返回值*1024就可以得到具体占用字节数 print(collectgarbage("count&…

解释器模式:专为语言处理定制的模式

在软件开发中&#xff0c;解释器模式是一种特定的行为型设计模式&#xff0c;它用于定义一种语法&#xff0c;并提供一个解释器来解释这种语法或表达式。这种模式用于专门的情况&#xff0c;当有一个简单的语言需要解释时&#xff0c;它可以被用来表达实例的规则。本文将详细介…

mybaits(8)-缓存机制

缓存机制 1、mybatis缓存2、一级缓存2.1 开启一级缓存2.2 一级缓存失效 3、二级缓存3.1 开启二级缓存3.2 二级缓存什么时候失效3.3 二级缓存的相关配置 4、MyBatis集成EhCache 1、mybatis缓存 缓存&#xff1a;cache 缓存的作用&#xff1a;通过减少IO的方式&#xff0c;来提高…

C++项目——集群聊天服务器项目(十四)客户端业务

大家好~前段时间有些事情需要处理&#xff0c;没来得及更新&#xff0c;实在不好意思。 今天来继续更新集群聊天服务器项目的客户端功能&#xff0c;主要实现客户端业务&#xff0c;包括添加好友、点对点聊天、创建群组、添加群组、群组聊天业务&#xff0c;接下来我们一起来敲…

Promise简单概述

一. Promise是什么&#xff1f; 理解 1.抽象表达&#xff1a; Promise是一门新的技术(ES6规范) Promise是JS中进行异步编程的新解决方案(旧方案是单纯使用回调函数) 异步编程&#xff1a;包括fs文件操作&#xff0c;数据库操作(Mysql)&#xff0c;AJAX&#xff0c;定时器 2.具…

【opencv】示例-imagelist_creator.cpp 从命令行参数中创建一个图像文件列表(yaml格式)...

/* 这个程序可以创建一个命令行参数列表的yaml或xml文件列表 */ // 包含必要的OpenCV头文件 #include "opencv2/core.hpp" #include "opencv2/imgcodecs.hpp" #include "opencv2/highgui.hpp" #include <string> #include <iostream>…

24、链表-回文链表

思路&#xff1a; 回文链表就是两个指针各从首 尾 开始遍历&#xff0c;实时相等&#xff0c;那么就是回文链表&#xff0c;或者关于中线对称。 第一种方式 集合方式实现很简单不再赘述&#xff0c;代码如下 //直接使用一个栈来校验&#xff0c;回文正过来 逆过来 都一样&am…

Go——Goroutine介绍

一. 并发介绍 进程和线程 进程是程序在操作系统中一次执行过程&#xff0c;系统进程资源分配和调度的一个独立单位。线程是进程执行的实体&#xff0c;是CPU调度和分派的基本单位&#xff0c;它是比进程更小的能独立运行的基本单位。一个进程可以创建和撤销多个线程&#xff0c…