一、概述
1、时间复杂度:一个程序(算法实现)在计算机上从开始运行到结束耗费的时间,且随着输入数据量的不断增大(无限大),耗费时间的量级也不一样,通过时间复杂度来判定一个程序实现消耗时间的量级程度,从而判断该程序是否为最佳实现(程序算法无错,能够正常执行时前提)。
2、空间复杂度:一个程序(算法实现)实现过程中所需的存储空间,且随着输入数据量的不断增大(无限大),耗费存储空间的量级也不一样,通过空间复杂度来判定一个程序实现需要的存储空间的量级程度,从而判断该程序是否为最佳实现(程序算法无错,能够正常执行时前提)。
3、作为一名研发人员,在实现重大业务算法时,尤其是高并发场景的需求,必须要考虑算法的时间复杂度(首要考虑),次之考虑空间复杂度
二、时间复杂度
1、时间频度 (语句频度):
一个算法中语句执行的次数,T(n),n 为变量参数,代表输入得规模,也可理解为该算法实现的业务问题的规模
2、时间复杂度:T(n)=c * O(f(n)) (c为常数) ,则 O(f(n)) 是T(n)的同数量级函数,引入同数量级函数为了呈现时间频度的变化规律。
定义有: T(n)=O(f(n)),称O(f(n)) 为算法的渐进时间复杂度,简称时间复杂度。
示例: O(n^2+n +1) = O (5n^2+n+3) = O (100n^2 + n) = O ( n2 ) (当n趋向无穷大)
3、时间复杂度从小到大排序: Ο(1)<Ο(log2n)<Ο(n)<Ο(nlog2n)<Ο(n2)<Ο(n3)<…<Ο(2n)<Ο(n!)
4、时间复杂度的计算
(1)确定业务实现逻辑
(2)选择算法实现
(3)找出算法中的基本操作
(4)计算基本操作次数随着输入规模n(要解决的业务问题的规模)的变化,时间频度的数量级 f(n)
(5)得出时间复杂度O(f(n))
示例1: 冒泡排序的时间复杂度
如初始数组是正序,一趟扫描进行n-1趟排序,但无移动 即可完成排序 ,((重要)基本操作为:前一个数组元素与后一个数组元素对比)n-1次 ,得出O(n-1) ,时间复杂度为O(n)
如初始数组是反序,则需要进行n-1次趟排序,每趟排序都需要进行 n- index 次比较 (index 当前数组座标是第几个, 1<index<n-1 ),且每次都需要移动三次(n、 n+1 、temp),
由此得出基本操作为 ,比较: n(n-1)/2 , 移动 3n(n-1)/2 ,得出 O(n(n-1)/2 + 3n(n-1)/2) ,时间复杂度为 O(n^2)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public static void bubbleSort(int []arr) { int[] arr = {12,23,34,56,56,56,78}; for(int i =0;i<arr.length-1;i++) { for(int j=0;j<arr.length-i-1;j++) {//-1为了防止溢出 if(arr[j]>arr[j+1]) { int temp = arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp; } } } } |
示例2: 类九九乘法表 java循环嵌套实现 ,时间复杂度O(n^2)
注:关于排序算法
常用的排序方法有:交换排序(冒泡排序、快速排序)、选择排序(简单选择排序、堆排序)、插入排序(直接插入排序、希尔排序)、归并排序、基数排序
三、空间复杂度
1、空间复杂度 即 程序实现过程需要的存储空间,S(n)=O(f(n)),n 为变量参数,代表输入得规模,也可理解为该算法实现的业务问题的规模
其中,f(n)为语句关于n所占存储空间的函数。
2、随着服务器硬件资源的发展,空间复杂度显得不如时间复杂度重要了,所以没有特殊说明的情况下算法“复杂度”通常指的是时间复杂度。
3、一个算法的空间复杂度只考虑在运行过程中为局部变量分配的存储空间的大小:形参、局部变量
4、空间复杂度计算
示例:冒泡排序 arr 、 i 、j、 temp ,所以空间复杂度为 O(1)
示例2: 递归算法的空间复杂度:整个递归算法占用的堆栈空间 ,加入一次调用所占堆栈空间为 M ,递归调用次数为N,则整个递归算法占用的空间为 M*(N+1),然后再根据算法的规模n来计算空间复杂度
四、复杂度
时间复杂度,空间复杂度统称为算法的复杂度,一个算法在提升时间复杂度的同时,可能会造成空间复杂的性能下降,反之亦是如此,所以在实现一个算法时需综合考虑业务需求、使用场景、性能要求及实现语言的特性(java,php,python)