探讨算法的时间复杂度的观察

探讨算法的时间复杂度的观察

文章目录

    • 探讨算法的时间复杂度的观察
      • 什么是时间复杂度?
      • 代码基本运行次数函数
      • 代码渐进时间复杂度(大O表示法)
      • 区间求和
      • 取一个足够大的值!
      • 总结

什么是时间复杂度?

在我们日常的学习使用当中,各种搜索算法以及暴力求法往往更受我们的喜爱,因为暴力求法往往在逻辑上与我们个人的直觉更自洽,相较于其他使用技巧的算法更容易便于我们接受,虽然说使用“暴力”求法是能够把要求的功能实现的,但往往这些暴力求法在时间和空间上有着巨大的复杂度,并不适合用于每一个场景。

时间复杂度作为衡量一个算法好坏的重要指标,我们也不能忽视。衡量代码的好坏,要么就是它的代码运行速度,要么就是它的存储空间大小。

很多人说,现在的计算机已经如此发达,运行速度还会很慢吗?当然会,当用户输入的值n足够大时,就算是计算速度最快的计算机计算,运行速度也要很久!我们之所以很难感受到这种运行速度很慢的情况,因为我们身边使用的各种软件 算法都经过无数计算机科学前辈的改良,使得时间复杂度降低,优化了运行速度。

一段代码的时间消耗不仅受运行设备的影响,还受着代码运行次数的影响,每个人的运行设备不同,运行设备的影响我们自然无法预估,精确的运行时间我们也不能准确得到,但一段代码我们可以通过代码的运行次数来准确判断它的时间复杂度。

代码基本运行次数函数

我们来看下面这些代码,基于Python:

i = 1
print(i)
print(i+1)

上面该代码用一个函数来表达基本操作执行次数,(此处我们姑且认为print函数的使用次数) 就是T(n) = 2,是一个常数。

for i in range(n):
    print("Hello,world")
    print("I'm Moss")

上面该代码用一个函数来表达基本操作执行次数,就是T(n)=2n

while i<n:
    print(i)
    print("hello")
    i=i*2

分析上面这个代码,在每一次循环之后,我们都将i乘以2,这样基本操作执行次数就变为了T(n) = 2*logn

for i in range(n):
    for j in range(n):
        print("hello")
        print('bye~')

上面这个代码很容易理解,就是嵌套的循环里面进行print, 总的基本操作次数函数为T(n) = 2* n *n

同理,嵌套的越多,操作次数就再乘一个n即可。

for i in range(n):
    for j in range(n):
        for k in range(n):
            for l in range(n):
                print("hello")
                print('bye~')

上述代码的基本操作次数函数就可以表示为T(n) = 2*n *n *n *n

代码渐进时间复杂度(大O表示法)

直白的讲,大O表示法就是将时间复杂度简化为一个数量级。

如果运行时间是常数级,就表示为1。有函数表达式的,只保留最高阶项,如果最高阶项存在,省去最高阶前面的系数。

第一段代码:O(n) = 1

第二段代码:O(n) = n

第三段代码:O(n) = logn

第四段代码:O(n) = n^2

大O表示法表示的也就是我们一致认为的代码时间复杂度

即我们可以通过大O表示法表达的函数的数学角度来大致分析代码的运行时间。很明显,在数学里面

1< logn

我们也就得出了每个算法的时间复杂关系

区间求和

下面,我们通过一道题目来直观理解时间复杂度。

题源:蓝桥杯算法竞赛 - 树木维护费用

大致题目:给出一段数组A,A[i]代表第i颗树的维护费用,请你计算一段区间,得出这个区间的总维护费用。

一眼看过去很简单,嵌套两个循环计算sum总值即可,但是因为时间限制在赛场只能过不到一半的案例。

利用前缀和算法即可降低时间复杂度(前缀和算法可以自行上网了解):

# 前缀和利用dp来优化区间求和,将T(n*m)的基本运行次数降低为T(n+m) 如果n和m的值足够大,时间复杂度相差很大

# 大学里树木要维护

n,m = map(int,input().split())
a = [0]+list(map(int,input().split()))
c = [list(map(int,input().split())) for i in range(m)]
dp = [0 for i in range(n+1)]

for i in range(1,n+1):
    dp[i] = dp[i-1]+a[i]

for i in c:
    print(dp[i[1]]-dp[i[0]-1])

print(a)
print(dp)

很明显,时间复杂度为O(n)

暴力求法如下:(不能通过所有案例)

## 暴力求法 区间求和
#更直观看出时间复杂度,不用sum()python内置函数
#大学树木要维护

n,m = map(int,input().split())
a = [0] + list(map(int,input().split()))
c = [list(map(int,input().split())) for i in range(m)]

for i in c:# m
    sums = 0
    for j in range(i[0],i[1]+1): #n
        sums += a[j] 
    print(sums)
## 代码基本运行次数T(n*m),代码时间复杂度O(n^2) 

很明显,时间复杂度为O(n^2)

可能你会说,当我随便向里面敲几个值时,感觉它们两个算法得出的结果速度是一样的呀?那是因为你的值默认了取一个你认为合适的值,如果值足够大呢?

取一个足够大的值!

这也是一个技巧,当你认为两个算法的时间复杂度相差不大的时候,仔细研究时间复杂函数的图像,当你取一个足够大的值时,高效与低效之间就很直观显现出来了。

如果n取100000000000

O(n) = n = 100000000000

O(n^2) = n* n = 100000000000*100000000000 = 10000000000000000000000

很明显,大了很多很多。

总结

分析一个算法的时间复杂度至关重要,时间复杂度也是衡量一个算法好坏的标准,在日常使用,各种竞赛中都要学会了解时间复杂度,优化时间复杂度。

你可能感兴趣的:(学习笔记,算法,python,开发语言)