时间复杂度计算及空间复杂度计算
目录
- 1.算法效率
- 2.时间复杂度
- 3.空间复杂度
- 4.大O渐进表示法
- 5.常见时间复杂度
- 常见复杂度对比
- oj练习
1.算法效率
算法效率分析分为两种:第一种是时间效率,第二种是空间效率。时间效率被称为时间复杂度,而空间效率被称作空间复杂度。 时间复杂度主要衡量的是一个算法的运行速度,而空间复杂度主要衡量一个算法所需要的额外空间,在计算机发展的早期,计算机的存储容量很小。所以对空间复杂度很是在乎。但是经过计算机行业的迅速发展,计算机的存储容量已经达到了很高的程度。所以我们如今已经不需要再特别关注一个算法的空间复杂度。
2.时间复杂度
2.1 时间复杂度的概念
时间复杂度的定义:在计算机科学中,算法的时间复杂度是一个
函数(数学中带有未知表达式的函数),它定量描述了该算法的运行时间。一个算法执行所耗费的时间,从理论上说,是不能算出来的,只有你把你的程序放在机器上跑起来,才能知道。但是我们需要每个算法都上机测试吗?是可以都上机测试,但是这很麻烦,所以才有了时间复杂度这个分析方式。一个算法所花费的时间与其中语句的执行次数成正比例,算法中的基本操作的执行次数,为算法的时间复杂度。
3.空间复杂度
空间复杂度是对一个算法在运行过程中临时占用存储空间大小量度 。空间复杂度不是程序占用了多少bytes的空间,因为这个也没太大意义,所以空间复杂度算的是变量的个数。空间复杂度计算规则基本跟实践复杂度类似,也使用大O渐进表示法。
4.大O渐进表示法
- 大O符号(Big O notation):是用于描述函数渐进行为的数学符号。推导大O阶方法:
- 1、用常数1取代运行时间中的所有加法常数。
- 2、在修改后的运行次数函数中,只保留最高阶项。
- 3、如果最高阶项存在且不是1,则去除与这个项目相乘的常数。得到的结果就是大O阶。
另外有些算法的时间复杂度存在最好、平均和最坏情况:
最坏情况:任意输入规模的最大运行次数(上界) 平均情况:任意输入规模的期望运行次数
最好情况:任意输入规模的最小运行次数(下界) 例如:在一个长度为N数组中搜索一个数据x 最好情况:1次找到 最坏情况:N次找到
平均情况:N/2次找到 在实际中一般情况关注的是算法的最坏运行情况,所以数组中搜索数据时间复杂度为O(N)
5.常见时间复杂度
计算一下这个算法的时间复杂度
// 请计算一下Func1基本操作执行了多少次?
void Func1(int N) {int count = 0;
//两层循环嵌套外循环执行n次,内循环执行n次,整体计算就是N*N的执行次数
for (int i = 0; i < N ; ++ i)
{for (int j = 0; j < N ; ++ j){++count;}
}
//2 * N的执行次数
for (int k = 0; k < 2 * N ; ++ k)
{++count;
}
//常数项10
int M = 10;
while (M--){++count; }printf("%d\n", count);}
精确的时间复杂度是N ^ 2 + 2 * N + 10
大O的渐进表示法时间复杂度是O(N ^ 2)
分析:
1、两层循环嵌套外循环执行n次,内循环执行n次,整体计算就是N*N 的执行次数
2、2 * N的执行次数
3、常数项10
根据前面的大o渐进表示法规则,所以最后只保留那项对执行次数影响最大的那一项,时间复杂度就是O(N ^ 2)
计算一下这个算法的时间复杂度
// 计算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);
}
精确的算法执行次数是2 * n + 10
采用大O渐进表示法规则后时间复杂度是O(N)
如果最高阶项存在且不是1,则去除与这个项目相乘的常数,那保留的就会是O(N)
计算一下这个算法的时间复杂度
// 计算Func3的时间复杂度?
void Func3(int N, int M)
{//执行M次int count = 0;for (int k = 0; k < M; ++ k) {++count; }//执行N次for (int k = 0; k < N ; ++ k) {++count; }printf("%d\n", count);
}
时间复杂度:O(M + N)
假设:
M远大于N --> O(M)
N远大于M --> O(N)
M和N一样大 --> O(M) / O(N)
还是取影响最大的那一项,如果并没有说明M和N的大小关系,那么时间复杂度就是O(M + N)
计算一下这个算法的时间复杂度
// 计算Func4的时间复杂度?
void Func4(int N){int count = 0;for (int k = 0; k < 100; ++ k) {++count; }printf("%d\n", count);
}
时间复杂度:O(1)
循环执行的次数是常数次,常数取1
计算一下这个算法的时间复杂度
// 计算strchr的时间复杂度?
const char * strchr ( const char * str, int character )
{while(*str){if(*str == character)return *str;elsestr++;}
}
时间复杂度:O(N)
最坏情况:遍历到最后才找到或者字符串中压根就没有,O(N)
平均情况:O(N / 2) , 忽略系数项是O(N)
最好情况:一次就找到了,O(1)
计算一下这个算法的时间复杂度
// 计算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; }}
精确的时间复杂度:O(N * (N-1) / 2)
冒泡排序的思想是:假设给你N个元素,让你排升序,对N个元素排升序,只需要执行N - 1趟,两两相比较,每趟交换都会将最大的那个元素换到最后面去,这样子一来最大的数就都集中在后面,不就相当于每排序一趟后就少比较一个元素嘛,因为后面已经是最大的了不需要再交换。
通过公式展开后采用大O渐进表示法规则后,时间复杂度:O(N ^ 2)
计算一下这个算法的时间复杂度
// 计算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;elsereturn mid; }return -1;
}
先来理解二分查找的思想:
二分查找是通过下标来搜索对应的元素值,前提是这个数组是有序的,通过确定中间位置划分左右区间,如果val小于mid那么目标值排除了出现在右边的情况,继续再左边区间查找,确定中间下标,通过中间下标划分左右区间,直到找到,否则一直重复规则直到循环结束,理解了二分查找的思想我们能想到的几种情况
最坏情况:O(log N)
每次确定中间下标,划分左右区间的时候都会除以2,那么
N / 2
N / 4
N / 8
.
.
.
1 //等于1的时候找到了
当找到了,要想知道它的执行次数的时候,通过它的展开就能知道
1 * 2 * 2 * 2 * 2 = N
2 ^ x = N
x = log N (以2为底,N的对数)
平均情况:不考虑
最好情况:O(1)
计算一下这个算法的时间复杂度
// 计算阶乘递归Factorial的时间复杂度?
long long Factorial(size_t N)
{return N < 2 ? N : Factorial(N-1)*N;
}
时间复杂度:O(N)
这个算法,递归的开始条件是当N >= 2,它的结束条件是N < 2,当条件成立的时候会一直递归下去,直达条件为假递归终止才会将结果返回,递归的深度决定的是算法的时间复杂度,
N - 1
N - 2
N - 3
.
.
2
1 //递归结束,递归的层数是N
计算一下这个算法的时间复杂度
// 计算斐波那契递归Fibonacci的时间复杂度?
long long Fibonacci(size_t N)
{return N < 2 ? N : Fibonacci(N-1)+Fibonacci(N-2);
}
时间复杂度:O(2 ^ N)
经典的递归分治算法,读者可以将它看成一个完全二叉树,在它递归层层展开的时候开辟的栈帧看作是一个结点,每创建一个结点都是一次函数调用,那么在脑海中想象出来的场景是这样的,可以看到它的调用次数是 Fib(N) = 2 ^0 + 2 ^1 + 2 ^ 2 + … + 2 ^(n -1) - x
时间复杂度:O(2 ^ N)
计算一下这个算法的空间复杂度
// 计算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;}
}
空间复杂度:O(1),常数个额外的变量
计算一下这个算法的空间复杂度
// 计算Fibonacci的空间复杂度?
long long* Fibonacci(size_t n) {if(n==0)return NULL;long long * fibArray =(long long *)malloc((n+1) * sizeof(long long));fibArray[0] = 0;fibArray[1] = 1;for (int i = 2; i <= n ; ++i){fibArray[i ] = fibArray[ i - 1] + fibArray [i - 2];}return fibArray ;
}
空间复杂度O(N),malloc了N+1个long long大小的空间
计算一下这个算法的空间复杂度
long long Factorial(size_t N)
{return N < 2 ? N : Factorial(N-1)*N;
}
空间复杂度O(N),之前说时间复杂度的时候讲这个例子是每调用一次这个函数就会开辟一次栈帧,调用了N次
long long Fib(size_t N)
{if(N < 3)returnelsereturn Fib(N - 1) + Fib(N - 2);
}
空间复杂度是O(N)
再之前讲时间复杂度的时候用这个例子,说的是将它看成一个完全二叉树,在它递归层层展开的时候开辟的栈帧看作是一个结点,每创建一个结点都是一次函数调用,当然计算时间复杂度符合这个特性,因为时间是不可复用的,而如果以空间复杂度的角度来计算的话,结果却并不是这样子,因为空间是可以复用的,每次递归调用,当Fib(N-1)递归完了,就会将内存归还给操作系统,进而才递归Fib(N - 2),所以空间复杂度是O(N)
常见复杂度对比
oj练习
17.04. 消失的数字
链接: link.
题目描述:
数组nums包含从0到n的所有整数,但其中缺了一个。请编写代码找出那个缺失的整数。你有办法在O(n)时间内完成吗?
- 思路一:快速排序 时间复杂度是O(N * log N),并不符合题目要求
- 思路一:0 ~N个数字全部加起来再减去数组中的所有数字加起来,得到的结果就是缺失的那个数,时间复杂度O(N)
- 思路三:内存映射,将数组元素值出现的次数放在对应的一个数组下标位置,时间复杂度是O(N)
- 思路四:异或,将m与0 ~ numsize个数字和0 ~ numsize数组元素异或,最后再与数组长度异或,时间复杂度是O(N)
//实现思路四:
int missingNumber(int* nums, int numsSize){int m = 0;int i = 0;while(i < numsSize){m ^= i;m ^= nums[i++];}m ^= numsSize;return m;
}
旋转数组
链接: link.
题目描述:
给定一个数组,将数组中的元素向右移动 k 个位置,其中 k 是非负数
实现思路:
数组元素整体逆置,【0,numsSize - 1】
左区间逆置,【0,k - 1】
右区间逆置,【k,numsize - 1】
//逆置数组
void reverse(int* nums1,int *nums2)
{while(nums1 < nums2){int tmp = *nums1;*nums1 = *nums2;*nums2 = tmp;++nums1;--nums2;}}void rotate(int* nums, int numsSize, int k){if(k >= numsSize)k %= numsSize;reverse(nums, nums + numsSize - 1);reverse(nums,nums + k - 1);reverse(nums + k,nums + numsSize - 1);}
时间复杂度计算及空间复杂度计算相关推荐
- 数据结构--时间复杂度计算和空间复杂度计算篇
时间复杂度和空间复杂度 一.时间复杂度 1.定义 2.三种时间复杂度 3.计算 *计算 #计算步骤 #常用技巧 #例题解析 二.空间复杂度 #定义 *计算 #计算步骤 #常用技巧 #例题解析 一.时间 ...
- 时间复杂度和空间复杂度计算(数据结构C语言实现1)
写在前面 数据结构C语言系列,和博主一起学习简单的数据结构和算法,都是利用C语言实现,很基础,相信大家都能够学会,都有所收获.记得点赞,收获. 目录 写在前面 本节目标 基本概念 **常见算法的时间复 ...
- 时间复杂度和空间复杂度计算
目录 1.什么是时间复杂度和空间复杂度 1.1 时间复杂度 1.2 空间复杂度 2. 如何计算时间复杂度和空间复杂度 2.1 使用大O阶方法 2.2 计算一些常用算法的时间复杂度 2.3 计算一些常用 ...
- (二) 时间/空间复杂度计算
时间/空间复杂度计算 时间复杂度是什么? O(1) O(n) O(1)+O(n)=O(n) O(n)*O(n)=O(n^2) O(logN) 空间复杂度是什么? O(1) O(n) O(n^2) 思考 ...
- 第2章 时间空间复杂度计算
1时间复杂度计算 时间复杂度是什么? 一个函数,用大O表示,例如:O(1), O(N), O(logN). 定性描述算法的运行时间. 时间复杂度常见图: 案例: O(1) let i = 0 i += ...
- 空间复杂度计算超全整理!!(一起手撕复杂度计算
承接上文:算法效率与时间复杂度(8条消息) 时间复杂度计算超全整理!!(数据结构和算法的第一步_vpurple__的博客-CSDN博客 目录 0.前言 1.空间复杂度 1.1 大O的渐进表示法 1.2 ...
- 时间和空间复杂度计算
目录 本章目标 理解时间空间复杂度 计算时间复杂度 计算空间复杂度 计算练习 1.1常见时间复杂度计算举例 例1 例2 例3 例4 例5 1.2常见空间复杂度的计算 例1 例2 例3 本章目标 1.什 ...
- 时间复杂度的规则与计算
1. 分析算法时,存在几种可能的考虑: 算法完成工作最少需要多少基本操作,即最优时间复杂度 算法完成工作最多需要多少基本操作,即最坏时间复杂度 算法完成工作平均需要多少基本操作,即平均时间复杂度 对于 ...
- 重学数据结构与算法(02)--将时间复杂度转换成空间复杂度
将时间复杂度转换成空间复杂度 1)时间昂贵.空间廉价 2)数据结构连接时空 3)降低复杂度的案例 例一 例二 4)总结 面试的过程中,常常会遇到考察手写代码的场景,通常面试官会追问:"这段代 ...
最新文章
- Mysql 存储引擎有哪些以及MyISAM和InnoDB区别
- c++访问控制说明符
- [redis] 分布式 Redis 的 CRUD 实现
- 通过电话号码获取姓名 (+86或者飞信)
- 大三软件工程小项目-小技术集合-tcp服务器搭建及客户端
- 上海市消保委:春节长假期间 共受理消费者投诉4600件
- 4.json字符串转换集json对象、json对象转换json字符串
- asp.net core 系列之并发冲突
- FreeType字体程序库介绍
- 《嵌入式 - 语音识别TWen-ASR-ONE开发笔记》第2章 TWen-ASR-ONE开发环境搭建与使用
- V神最新演讲:万字长文透露以太坊20重大升级!
- 基恩士KV8000程序 基恩士KV8000,威伦通触摸屏,搭载KV-XH16EC总线模块进行分布式总线控制
- c语言判断正整数x是否为同构数,c语言上机题库(阅读).doc
- NUL与NULL的区别
- JQuery中常用的 属性选择器
- Windows构建Flutter环境,无法访问maven.google.com
- K8S - 安装教程 和 体验(kubeadm)
- 传奇服务器脚本文件在哪个文件夹,传奇服务端各个文件夹目录结构详细说明表...
- 【CEGUI】CEGUI入门篇之数据文件及默认初始化(三)
- RB521C30-2/TR肖特基势垒二极管WILLSEM封装SOD923