“算法复杂度”其实并没有那么复杂
算法是用于解决特定问题的一系列的执行步骤。使用不同算法,解决同一个问题,效率可能相差非常大。为了对算法的好坏进行评价,我们引入 “算法复杂度” 的概念。
1、引例:斐波那契数列(Fibonacci sequence)
已知斐波那契数列:,求它的通项公式
。
求解斐波那契数列的方法有很多种,这里只介绍两种:递归法和平推法。
package com.atangbiji;public class Main {public static void main(String[] args) {// 输出通项F(n)System.out.println(fib1(1));System.out.println(fib1(2));System.out.println(fib1(3));System.out.println(fib1(4));System.out.println(fib1(5));System.out.println(fib2(70));}/** 求斐波那契数列(Fibonacci sequence)* F(1)=1,F(2)=1, F(n)=F(n - 1)+F(n - 2)(n ≥ 3,n ∈ N*)的通项F(n).*//** 方法一:递归法* 最高支持 n = 92 ,否则超出 Long.MAX_VALUE* @param n * @return f(n) * */public static long fib1(int n) {if (n < 1 || n > 92)return 0;if (n < 3)return 1;return fib1(n - 1) + fib1(n - 2);}/** 方法二:平推法* 最高支持 n = 92 ,否则超出 Long.MAX_VALUE* @param n * @return f(n) * */public static long fib2(int n) {if (n < 1 || n > 92)return 0;//n: 1 2 3 4 5 ……//F(n): 1 1 2 3 5 ……long first = 1;long second = 1;for (int i = 3; i <= n; i++) {long sum = first + second;first = second;second = sum;}return second;}}
通过测试,我们可以发现:当n的取值较大时(如:n = 60),若采用递推法计算则会发现迟迟不出结果,若采用平推法计算则可以秒出结果。由此可见, 平推法的效率明显高于递推法。
2、如何评估算法的好坏?
正确性
可读性
健壮性:对不合理输入的反应能力和处理能力。
时间复杂度(time complexity): 估算程序指令的执行次数(执行时间)。
空间复杂度(space complexity): 估算所需占用的存储空间。
注:一般情况下,我们主要考虑算法的时间复杂度。 (因为目前计算机的内存一般都比较大)
3、时间复杂度的估算
我们可以用程序指令的执行次数来估算时间复杂度。例如:
(1)函数test1
public static void test1(int n) {//总执行次数 = 14// 1(判断语句可以忽略)if (n > 10) {System.out.println("n > 10");} else if (n > 5) {System.out.println("n > 5");} else {System.out.println("n <= 5");}// 1 + 4 + 4 + 4for (int i = 0; i < 4; i++) {System.out.println("test");}
}
(2)函数test2
public static void test2(int n) {//总执行次数 = 1 + 3n//1 + n + n + nfor (int i = 0; i < n; i++) {System.out.println("test");}
}
(3)函数test3
public static void test3(int n) {//总执行次数 = 48n + 1// 1 + 2n + n * (1 + 45)for (int i = 0; i < n; i++) {for (int j = 0; j < 15; j++) { // 1 + 15 + 15 + 15System.out.println("test");}}
}
(4)函数test4
public static void test4(int n) {//总执行次数 = 3n^2 +3n +1// 1 + 2n + n * (1 + 3n)for (int i = 0; i < n; i++) {for (int j = 0; j < n; j++) { // 1 + n + n + nSystem.out.println("test");}}
}
(5)★ 函数test5
public static void test5(int n) {//总执行次数 = log2(n)/** n = 2 , 执行 1 次* n = 4 , 执行 2 次* n = 8 , 执行 3 次 * */while ((n = n/2) > 0) { // 倍速减小System.out.println("test"); // 只考虑这一句的执行次数}
}
(6)函数test6
public static void test6(int n) {//总执行次数 = log5(n)while ((n = n/5) > 0) {System.out.println("test"); // 只考虑这一句的执行次数}
}
(7)函数test7
public static void test7(int n) {//总执行次数 = 3n*log2(n) + 3log2(n) + 1// 1 + 2 * log2(n) + log2(n) * (1 + 3n)/** n = 2 , 执行 1 次* n = 4 , 执行 2 次* n = 8 , 执行 3 次 * */for (int i = 1; i < n; i += i) { // i = i + i = i * 2(倍速增大)for (int j = 0; j < n; j++) { // 1 + n + n + nSystem.out.println("test");}}
}
4、大O表示法
为了进一步简化复杂度的计算,我们一般使用大O表示法来描述时间(或空间)复杂度。它表示的是 数据规模为n 时算法所对应的复杂度。
大O表示法的性质:
(1)可以忽略常数、常系数和低阶项。
-
(
)
-
(
)
-
(
)
(2)对数阶一般省略底数,统称
。
注:大O表示法仅仅只是一种粗略的分析模型,是一种估算。 它能帮我们快速了解一个算法的执行效率。
5、常见的复杂度
其中:
当数据规模较小时, 各复杂度对应的曲线如下图所示。
当数据规模较大时, 各复杂度对应的曲线如下图所示。
所以,当数据规模比较大时,复杂度为
我们就很难接受了。
6、斐波那契数算法复杂度分析
(1)递归法
public static long fib1(int n) {if (n < 1 || n > 92)return 0;if (n < 3)return 1;return fib1(n - 1) + fib1(n - 2);
}
假设计算
时 和 的值已经得到,我们可以发现该函数每次执行的时间主要取决于求和运算。因此,该算法函数指令的执行次数等价于该函数被递归调用次数。
当
时,该函数的调用过程如下图所示。
所以,该函数被递归调用的次数
二叉树的节点数。
即:。
因此,该算法的复杂度为
。
注: 细心的同学可能会发现,当
时,函数被递归调用的次数并不完全等于 。
这里需要说明的是:复杂度是一种估算,我们关心的不是具体的数值,而是量级和趋势。 所以,
呈指数级增长的趋势是毋庸置疑的。
(2)平推法
public static long fib2(int n) {if (n < 1 || n > 92)return 0;//n: 1 2 3 4 5 ……//F(n): 1 1 2 3 5 ……long first = 1;long second = 1;for (int i = 3; i <= n; i++) {long sum = first + second;first = second;second = sum;}return second;
}
显然,平推法的时间复杂度为
。
7、算法的优化方向
(1)用尽量少的执行步骤(运行时间)。
(2)用尽量少的存储空间。
(3)根据情况,空间换时间或者时间换空间。
更多关于复杂度的知识,我们会在后续数据结构和算法的设计与实现过程中穿插讲解。
(本讲完,系列博文持续更新中…… )
参考文献:
《恋上数据结构与算法》,小码哥MJ
《数据结构与算法》,邓俊辉
更多Java和算法教程,关注下面公众号:
“算法复杂度”其实并没有那么复杂相关推荐
- 20162311 算法复杂度-3
算法复杂度-3 题目要求 实现情况 求一个整数二维数组Arr[N][N]的所有元素之和 我自己写了一个getSum的方法 算法的时间复杂度为O(n^2) 对于输入的任意 3 个整数, 将它们按从小到大 ...
- PHP复杂度,php 算法复杂度 时间复杂度 空间复杂度
算法复杂度分为时间复杂度和空间复杂度. 其作用: 时间复杂度是指执行算法所需要的计算工作量: 而空间复杂度是指执行这个算法所需要的内存空间. (算法的复杂性体现在运行该算法时的计算机所需资源的多少上, ...
- .NET平台BigO算法复杂度备忘
之前一篇文章提到BIG O算法复杂度的备忘录, 今天这个是.NET 平台下集合类相关的Big O 算法复杂度 今天先到这儿,希望对您有参考作用, 您可能感兴趣的文章: 数据结构与算法 Big O 备忘 ...
- O(n)级选排名第k位数(附上算法复杂度分析)
算法简述 如果想要拿到第k位,一般说复杂度都比较高.例如,用快排等方式,要用了O(nlogn)水平的时间复杂度.就算是用快排改进,每次在快排的基础上,只排剩下的一部分,在平均水平上,也会变成了O(nl ...
- “算法复杂度”——其实并没有那么复杂
算法是用于解决特定问题的一系列的执行步骤.使用不同算法,解决同一个问题,效率可能相差非常大.为了对算法的好坏进行评价,我们引入 "算法复杂度" 的概念. 1.引例:斐波那契数列(F ...
- 二分检索用途及复杂性_二分查找和三分查找哪个快?算法复杂度与常数无关?复杂度分析的常见误区...
还记得两三年前,我初看一本算法书,看到二分查找算法的复杂度时,我发现了了不得的东西:二分查找每次查询范围减少一半,需要查询的次数是 ,它的复杂度是 . 我把它改成三分查找,每次查询两个数字与我的目标数 ...
- 算法复杂度分析(下)
前一篇文章算法复杂度分析(上)讲述了复杂度的大 O 表示法和几个分析原则,这篇文章我们来讲讲另外几种复杂度,最好情况时间复杂度(best case time complexity).最坏情况时间复杂度 ...
- 排序代码(python,c++) 及 基本算法复杂度
0.导语 本节为手撕代码系列之第一弹,主要来手撕排序算法,主要包括以下几大排序算法: 直接插入排序 冒泡排序 选择排序 快速排序 希尔排序 堆排序 归并排序 1.直接插入排序 [算法思想] 每一步将一 ...
- 老王带你理解算法复杂度O(1),O(N),O(N^2)
上图对应的是算法复杂度的图片,X轴对应的是n(问题规模),Y轴对应的是执行的运行时间. 我们先从简单的复杂度解读O(1) 从上面的图片我们可以看到O(1)的复杂度是恒定的,一点波澜都没有,什么是O(1 ...
- 八大排序:Java实现八大排序及算法复杂度分析
目录 QUESTION:八大排序:Java实现八大排序及算法复杂度分析 ANSWER: 一:冒泡排序 1.算法分析 2.时间复杂度分析 3.代码 二:选择排序 1.算法分析 2.时间复杂度分析 3.代 ...
最新文章
- Android手机系统adb常用的命令
- cuda安装和caffe
- gulp复制整个文件夹或文件到指定目录(包括拷贝单个文件)
- Python爬虫 - 解决动态网页信息抓取问题
- 2020 年微服务项目活跃度报告
- mysql bin.000013_"mysql-bin.0000*"占用空间问题及删除
- Python程序开发——第二章 条件语句和循环语句
- windows xp下Apache2.2.11整合Tomcat6.0.20
- POJ1742Coins
- qq象棋辅助 android,QQ象棋自动下棋
- 分布式系统的阿喀琉斯之踵:数据一致性!
- java微信开发之--更换背景图片
- matlab怎么做多元非线性拟合,如何用matlab进行多元非线性拟合
- Spring 事务传播行为
- 牛客网 - [牛客假日团队赛6]对牛排序
- 诺基亚智能手机内存不足等问题的解决
- u深度制作linux启动盘制作工具,U深度U盘启动盘制作工具怎么操作?U深度U盘启动盘制作工具使用...
- Fluent中的各种初始化
- carla学习笔记(十)
- 武汉光庭导航面试经历
热门文章
- 【云原生】Kubernetes(k8s)部署 MySQL+Dubbo+Nacos服务
- 一、重写muduo网络库之服务器编程及测试
- java计算机毕业设计精准扶贫管理系统源程序+mysql+系统+lw文档+远程调试
- 浅学Matlab:确定比赛的胜负问题
- JAVA园林公司OA系统计算机毕业设计Mybatis+系统+数据库+调试部署
- 《绝地求生:大逃亡》赚4亿美元,中国玩家比例最高
- 基于spring 的开源框架
- tensorflow/serving部署keras的h5模型服务
- 2016最新H5面试题(及答案)
- 虽然只是个“数字助手”,却是AI的一次飞跃