蓝桥杯备赛笔记(一)——基础算法篇

蓝桥杯备赛笔记(一)——基础算法篇:算法时间复杂度和空间复杂度分析

一、基础算法篇

1.算法时间复杂度和空间复杂度分析

程序=数据结构+算法,数据结构与算法之间存在着本质联系。

1.1算法的定义及特性

算法( Algorithm )是为了解决某类问题而规定的一个有限长的操作序列。
一个算法必须满足以下5个重要特性。
(1)有穷性。一个算法必须总是在执行有穷步后结束,且每一步都必须在有穷时间内完成。
(2)确定性。对于每种情况下所应执行的操作,在算法中都有确切的规定,不会产生二义性,算法的执行者或阅读者都能明确其含义及如何执行。
(3)可行性。算法中的所有操作都可以通过已经实现的基本操作运算执行有限次来实现。

(4)输入。一个算法有0个或多个输入。当用函数描述算法时,输人往往是通过形参表示的,在它们被调用时,从主调函数获得输入值。
(5)输出。一个算法有一个或多个输出,它们是算法进行信息加工后得到的结果,无输出的算法没有任何意义。当用函数描述算法时,输出多用返回值或引用类型的形参表示。

1.2评价算法优劣的基本标准

(1)正确性。在合理的数据输入下,能够在有限的运行时间内得到正确的结果。
(2)可读性。一个好的算法,首先应便于人们理解和相互交流,其次才是机器可执行性。可读性强的算法有助于人们对算法的理解,而难懂的算法容易隐藏错误,且难于调试和修改。

(3)健壮性。当输人的数据非法时,好的算法能适当地做出正确反应或进行相应处理,而不会产生一些莫名其妙的输出结果。
(4)高效性。高效性包括时间和空间两个方面。时间高效是指算法设计合理,执行效率高,可以用时间复杂度来度量;空间高效是指算法占用存储容量合理,可以用空间复杂度来度量。时间复杂度和空间复杂度是衡量算法的两个主要指标。

1.3算法的时间复杂度

衡量算法效率的方法主要有两类:事后统计法和事前分析估算法。事后统计法需要把算法转换成可执行的程序,时空开销的测算结果依赖于计算机的软硬件等环境因素,这容易掩盖算法本身的优劣。所以我们通常采用事前分析估算法,通过计算算法的渐近复杂度来衡量算法的效率。

1.3.1问题规模和语句频度

不考虑计算机的软硬件等环境因素,影响算法时间代价的最主要因索是问题规模n。问题规模 n 对不同的问题含义不同,例如,在排序运算中 n 为参加排序的记录数,在矩阵运算中 n 为矩阵的阶数,在多项式运算中 n 为多项式的项数,在集合运算中 n 为集合中元素的个数,在树的有关运算中 n 为树的结点个数,在图的有关运算中 n 为图的顶点数或边数。显然, n 越大算法的执行时间越长。
算法的执行时间大致上等于其所有语句执行时间的总和,而语句的执行时间则为该条语句的重复执行次数和执行一次所需时间的乘积
一条语句的重复执行次数称作语句频度( Frequency Count )。
由于语句的执行要由源程序经编译程序翻译成目标代码,目标代码经装配再执行,因此语句执行一次实际所需的具体时间是与机器的软、硬件环境(如机器速度、编译程序质量等)密切相关的。所以,所谓的算法分析并非精确统计算实际的执行所需时间,而是针对算法语句的执行次数做出估计。从中得出算法执行时间的信息。

设每条语句式执行一次所需的时间均是单位时间,则一个算法的执行时间可用该算法中所有语句频度之和来度量。

1.3.2算法的时间复杂度定义

对于稍微复杂些的算法,计算所有语句的频度则通常是比较困难的。因此,为了客观地反映一个算法的执行时间,可以只用算法中的”基本语句“次数来度量算法的工作量。所谓"基本语句"指它算法运行时间的贡献最大。我们只考虑n充分大时,算法中基本语句的执行次数渐进的阶。
算法时间复杂度的定义:
一般情况下,算法中基本语句重复执行的次数是问题规模 n 的某个函数 f( n )、算法的 时间 度记作:
T ( n ) = O ( f ( n ) ) T ( n )=O(f(n)) T(n)=O(f(n))
用" O “来表示数量级。(1)表示随着问题规模 n 的增大,算法执行时间的增长率和f( n )的增长率相同,称作算法近时间复杂度,简称时间复杂度(Time Complexity )。
数学符号” O "的严格定义为:
若 T (n)和f(n)是定义在正整数集合上的两个函数,则 T (n)和O(f(n))表示存在正的常数 C 和 n ,使得当 n ≥ n0时都满足0≤ T (n)≤ Cf(n)。该定义说明了函数T (n)和f(n))具有相同的增长趋势,并且T (n)的增长至多趋向于函数f(n)的增长。符号" O "用来描述增长率的上限,它表示当问题规模 n > n0 。时,算法的执行时间不会超过f(n) 。

1.3.3算法的时间复杂度分析举例

分析算法时间复杂度的基本方法为:找出所有语句中语句频度最大的那条语句作为基句,计算基本语句的频度得到问题规模 n 的某个函数 f(n),取其数量级用符号" O "表示即可,具体计算数量级时遵循以下定理:若
f ( n ) = a m n m + a m − 1 n m − 1 + . . . + a 1 n + a 0 f(n) = a_mn^m+a_{m-1}n^{m-1}+...+a_1n+a_0 f(n)=amnm+am1nm1+...+a1n+a0
是一个m次多项式,则
T ( n ) = O ( n m ) T(n) = O(n^m) T(n)=O(nm)
可以忽略所有低次幂项和高次幂的系数。

常见的时间复杂度按数量级递增排列依次为:常量阶O(1)<对数阶O(log2n)<线性阶O(n)<线性对数阶O(n log2n)<平方阶O(n2)<立方阶O(n3) <…<k方阶O(nk)<指数阶O(2n)

1.3.4最好、最坏和平均时间复杂度

对于某些问题的算法,其基本语句的频度不仅仅与问题的规模相关,还依赖于其他因素。例如,顺序查找长度为n的一维数组某个元素,最好时间复杂度是第一次就找到为O(1),最坏时间复杂度最后一次找到为O(n),而对于一个算法来说,需要考虑各种可能出现的情况,以及每一种情况出现的概率。一般情况下,可假设待查找的元素在数组中所有位置上出现的可能性均相同,则可取语句的频度最好情况与最坏情况下的平均值,即 f ( n )= n /2,作为它的度量。
称算法在最好情况下的时间复杂度为最好时间复杂度,是指算法计算量可能达到的最小值;称算法在最坏情况下的时间复杂度为最坏时间复杂度,是指算法计算量可能达到的最大值;算法的平均时间复杂度是指算法在所有可能情况下,按照输入实例以等概率出现时,算法计算量的加权平均值
对算法时间复杂度的度量,人们通常只讨论的是最坏时间复杂度,分析最坏情况下,算法执行时间的上界

1.4算法的空间复杂度

S ( n ) = O ( f ( n ) ) S(n)= O(f(n)) S(n)=O(f(n))

除了寄存本身所用的指令常数、变量和输入数据外,还需要一些对数据进行操作的辅助存储空间。其中,输入数据所占的具体存储量取决于问题本身,与算法无关,这样只需分析该算法在实现时所需要的辅助空间就可以了。若算法执行时所需要的辅助空间相对于输入数据量而言是个常数,则称这个算法在原地工作,辅助空间为 O (1)。

对于一个算法,其时间复杂度和空间复杂度往往是相互影响的,当追求一个较好的时间复杂度时,可能会导致占用较多的存储空间,即可能会使空间复杂度的性能变差,反之亦然。不过,通常情况下,鉴于运算空间较为充足,人们都以算法的时间复杂度作为算法优劣的衡量指标。

2.枚举算法

枚举算法(Enumeration Algorithm):核心思想是暴力枚举问题所涉及的所有可能情况,不能遗漏和重复,并根据问题提出的条件逐个比较得出问题的解。故也叫穷举法。

意义:枚举可作为某类问题时间性能的底线,用来引出同样问题的更高效率的算法。

可以充分利用计算机的速度,解决一些常见问题。

优缺点:优点:简单暴力。缺点:当问题规模较大时,效率一般会比较低。通常用于求解问题规模比较小的问题

应用条件:枚举的所有可能情况是有限的并可全部被预估。

下列有几道枚举类型的例题:

题一:有一个 n*m方格的棋盘,求棋盘方格包含多少正方形、长方形。
在这里插入图片描述
解题思路:1.先求矩形的个数,再求出正方形的个数,长方形的个数=矩形的个数-正方形的个数。

2.方法一(枚举点的组合)矩形的个数,画出一个棋盘,我们不难发现,棋盘中各取长和宽上的两个点可组成一个矩形,于是我们分别枚举出长和宽上两个点的组合 C ( k n ) = n ! k ! ( n − k ) ! C\binom{k}{n} = \frac{n!}{k!(n-k)!} C(nk)=k!(nk)!n! C ( k m ) = m ! k ! ( m − k ) ! C\binom{k}{m} = \frac{m!}{k!(m-k)!} C(mk)=k!(mk)!m!进行相乘可得矩形的个数。(方法一枚举点的组合)。方法二(枚举边):也可以枚举棋盘长和宽的满足组成矩形长和宽的所有情况。

3.正方形的个数,根据最短的一边确定正方形的最大边长,枚举出正方形所有可能的边长情况,在每种边长的正方形下,分别枚举出棋盘长和宽上满足正方形边长的所有的情况,两者进行相乘,可得该种边长的正方形有多少个,循环相加即可得正方形的数量。

4.长方形的个数=矩形的个数-正方形的个数。

方法一代码如下:

def factorial(n):  # 计算一个数的阶乘
    result = 1
    for i in range(1, n + 1):
        result *= i
    return result


n = int(input("请输入棋盘的n值"))
m = int(input("请输入棋盘的m值"))
square = 0
s = (factorial(m + 1) / (factorial(2) * factorial(m + 1 - 2))) * (
        factorial(n + 1) / (factorial(2) * factorial(n + 1 - 2)))
print("棋盘包含矩形的总数:", s)
for i in range(1, min(n, m) + 1):
    square += (n - i + 1) * (m - i + 1)
print("棋盘包含正方形的数量:", square)
print(f"棋盘包含长方形的数量:{s - square}")

方法二代码如下:

n = int(input("请输入棋盘的n值"))
m = int(input("请输入棋盘的m值"))
square = 0
a = 0
b = 0
for i in range(1, m + 1):
    a += m - i + 1
for i in range(1, n + 1):
    b += n - i + 1
s = a * b
print("棋盘包含矩形的总数:", s)
for i in range(1, min(n, m) + 1):
    square += (n - i + 1) * (m - i + 1)
print("棋盘包含正方形的数量:", square)
print(f"棋盘包含长方形的数量:{s - square}")

题二:2023
请求出在12345678(含)至98765432(含)中,有多少个数中完全不包含2023。完全不包含2023是指无论将这个数的哪些数位移除都不能得到2023。例如20322175,33220022都完全不包含2023,而20230415,20193213则含有2023(后者取第1,2,6,8个数位)。
具体解决之前已经写了一篇,请点击下方链接参考细节:
2023年十四届省赛大学B组真题第一题:2023

文章持续更新中,如果您觉得对您有帮助,点个赞再走吧!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

疯狂程序员花椒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值