几个常见的时间复杂度

1.时间复杂度定义

算法的时间复杂度是一个函数,它定性描述该算法的运行时间。这是一个代表算法输入值的字符串的长度的函数。时间复杂度常用大O符号表述,不包括这个函数的低阶项和首项系数。使用这种方式时,时间复杂度可被称为是渐近的,亦即考察输入值大小趋近无穷时的情况。
简单来说就是执行算法所需要的计算工作量

2.计算时间复杂度

O(1)

function fn1(){
	console.log(1)
	console.log(2)
}

函数fn1运行的时候执行了两次console,所以它的时间复杂度是2,用O表示为O(2),但对于常数项往往可以忽略不计,所以我们一般将常数项计算为1,就算是O(100)我们时间复杂度也用O(1)表示

O(n)

function fn2(n){
	for(let i =0;i<n;i++){
		console.log(n)
	}
}

函数fn2执行的时候,首先for中的let i = 0执行了一次,接着执行i < 0,然后执行console,再执行i++,以此类推到最后,i<0将执行n+1次console将执行n次i++执行n次,加上一开始的let i = 0,一共是1+n+1+n+n = 3n+2次,即O(n)=常数*n+常数,当n达到一定数量后,第二部分的常数可以忽略不计,即O(n)=常数*n,将剩下的常数当作该系数的常数作为1,得到最终时间复杂度O(n)

O( n 2 n^2 n2)

function fn3(n){
	for(let i = 0; i < n;i ++){
		for(let i = 0; i < n; i++){
			console.log(n)
		}
	}
}

根据上面的方法,我们可以推断出fn3的执行时间为 1 + n + 1 + n + n × ( 1 + n + 1 + n + 1 ) = 2 n 2 + 5 n + 2 1+n+1+n+n\times(1+n+1+n+1) =2n^2+5n+2 1+n+1+n+n×(1+n+1+n+1)=2n2+5n+2,根据刚提到的省略常量,以及将系数作为1处理则得到 n 2 + n n^2 +n n2+n,当n达到一定值时,相对于 n 2 n^2 n2,n可忽略所以最终得到时间复杂度为O( n 2 n^2 n2),当执行次数出现多个幂时,则取最高的,如 n 3 + n 4 + n 5 + 5 n^3+n^4+n^5+5 n3+n4+n5+5,则取 n 5 n^5 n5即可

O(log 2 n 2^n 2n)

function fn4(n){
	for(let i =1; i < n; i*=2){
		console.log(n)
	}
}

fn4当传入参数n为8时,首先i = 1,小于8成立,执行循环体内代码1次,i * 2 = 2,小于8继续成立,循环体内代码执行次数加一,i *2 = 4,小于8成立继续,循环体内执行第3次,接着i *2 = 8,小于8不成立循环结束,一共执行了3次。得到O(8) =3
2 3 = 8 2^3 = 8 23=8 3 = l o g 2 8 3 = log2^8 3=log28,由此可得到 O ( 8 ) = l o g 2 8 O(8) = log2^8 O(8)=log28

### 不同时间复杂度的比较及应用场景 #### 时间复杂度的概念 时间复杂度是用来描述算法运行所需时间的增长趋势的一个重要概念。它是衡量算法效率的关键指标之一,通常用大 O 符号来表示[^3]。 --- #### 常见时间复杂度及其特点 | **时间复杂度** | **名称** | **特点** | |-----------------|------------------------|--------------------------------------------------------------------------------------------| | \(O(1)\) | 常数时间 | 执行时间不随输入规模变化,是最高效的复杂度[^1]。 | | \(O(\log n)\) | 对数时间 | 随着输入规模增大,执行次数按对数比例增加,常用于二分查找等场景。 | | \(O(n)\) | 线性时间 | 执行次数与输入规模成正比,适用于简单遍历操作。 | | \(O(n \log n)\)| 线性对数时间 | 大多数高效排序算法(如快速排序、归并排序)都属于此类。 | | \(O(n^2)\) | 平方时间 | 双重循环结构,常见于冒泡排序、插入排序等低效算法。 | | \(O(2^n)\) | 指数时间 | 输入规模每增加一次,运算量翻倍,仅适合小规模问题,如某些动态规划问题或回溯法应用。 | --- #### 各类时间复杂度的应用场景 1. **\(O(1)\)** 应用于固定大小的操作,例如访问数组中的某个元素或哈希表查询。这种复杂度非常高效,在实际开发中应尽可能追求这一目标。 2. **\(O(\log n)\)** 主要应用于基于分治策略的算法,比如二分查找、堆排序以及一些树形数据结构(如平衡二叉树)。这类算法在处理大规模数据时表现出色。 3. **\(O(n)\)** 单层循环操作,典型例子包括线性扫描整个列表寻找特定值或者计算总和等问题。尽管不如前两者高效,但在许多情况下仍然可接受[^4]。 4. **\(O(n \log n)\)** 这一类别涵盖了大多数高级排序方法(如快速排序、归并排序),同时也涉及图论中最短路径算法(Dijkstra 使用优先队列版本)、拓扑排序等领域。 5. **\(O(n^2)\)** 属于较低效的一类,多出现在初学者编写的双重嵌套循环程序里,像经典的冒泡排序就是典型的代表。当面对较大尺寸的数据集时可能会显得力不从心。 6. **\(O(2^n)\)** 此级别下的解决方案往往针对组合爆炸型难题,例如旅行商问题(TSP),子集生成器等功能实现上会采用这种方法。然而由于其极端消耗资源特性,一般只限于极小型实例测试用途。 --- #### 示例代码展示 以下是几个具有代表性的时间复杂度对应的伪代码: ```python # O(1): 访问数组指定位置 def access_element(array, index): return array[index] # O(log n): 二分查找 def binary_search(sorted_array, target): low, high = 0, len(sorted_array)-1 while low <= high: mid = (low + high)//2 if sorted_array[mid] == target: return True elif sorted_array[mid] < target: low = mid + 1 else: high = mid - 1 return False # O(n): 简单遍历 def sum_of_elements(array): total = 0 for num in array: total += num return total # O(n log n): 归并排序 def merge_sort(array): if len(array) > 1: mid = len(array)//2 L = array[:mid] R = array[mid:] merge_sort(L) merge_sort(R) i = j = k = 0 while i < len(L) and j < len(R): if L[i] < R[j]: array[k] = L[i] i += 1 else: array[k] = R[j] j += 1 k += 1 while i < len(L): array[k] = L[i] i += 1 k += 1 while j < len(R): array[k] = R[j] j += 1 k += 1 # O(n^2): 冒泡排序 def bubble_sort(array): n = len(array) for i in range(n): for j in range(0, n-i-1): if array[j] > array[j+1]: array[j], array[j+1] = array[j+1], array[j] # O(2^n): 斐波那契递归版 def fibonacci_recursive(n): if n <= 1: return n return fibonacci_recursive(n-1) + fibonacci_recursive(n-2) ``` --- #### 总结 通过对上述各类时间复杂度的理解可以看出,不同的算法适应范围差异显著。因此,在具体项目实施过程中需综合考量实际情况选取合适的技术手段以达到最佳效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值