2024年最全Python数据结构与算法(1,字节跳动如何面试

最后

🍅 硬核资料:关注即可领取PPT模板、简历模板、行业经典书籍PDF。
🍅 技术互助:技术群大佬指点迷津,你的问题可能不是问题,求资源在群里喊一声。
🍅 面试题库:由技术群里的小伙伴们共同投稿,热乎的大厂面试真题,持续更新中。
🍅 知识体系:含编程语言、算法、大数据生态圈组件(Mysql、Hive、Spark、Flink)、数据仓库、Python、前端等等。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 掌握空间复杂度分析方法

  • 了解 Python 列表和字典常见操作的时间复杂度

1. 算法的设计要求


算法分析的主要目标是从运行时间和内存空间消耗等方面比较算法。

1.1 算法评价的标准

一个好的算法首先应该是“正确”的,其对于每个输入实例均能终止并给出正确的结果,能够正确解决给定的计算问题。此外,还需要考虑以下方面:

  • 高效性:执行算法所需要的时间;

  • 低存储量:执行算法所耗费的存储空间,其中主要考虑辅助存储空间;

  • 可读性:算法应易于理解,易于编码,易于调试等等。

1.2 算法选择的原则

一个算法同时可以满足存储空间小、运行时间短、其它性能也好是很难做到的,很多情况下,我们不得不对性能进行取舍,在实际选择算法时,我们通常遵循以下原则:

  • 若该程序使用次数较少,则力求算法简明易懂;

  • 对于反复多次使用的程序,应尽可能选用快速的算法;

  • 若待解决的问题数据量极大,机器的存储空间较小,则相应算法主要考虑如何节省空间。

2. 算法效率分析


算法效率分析根据算法执行所需的时间进行分析和比较,这也称为算法的执行时间或运行时间。要衡量算法的执行时间,一个方法就是做基准分析,这是一种事后统计的方法,其使用绝对的时间单位来记录程序计算出结果所消耗的实际时间。在 Python 中,可以使用 time 模块的 time 函数记录程序的开始时间和结束时间,然后计算差值,就可以得到以秒为单位的算法执行时间。

以计算斐波那契数列第 n 项为例(斐波那契数列从第3项开始,每一项都等于前两项之和),在计算斐波那契数列第 n 项前后调用 time 函数,计算执行时间:

import time

def fibo(n):

start = time.time()

a, b = 1, 1

if n > 2:

for i in range(n-2):

a, b = b, a + b

end = time.time()

running = end-start

return b, running

for i in range(5):

results = fibo(100000)

print(‘It takes {:.8f} seconds to calculate the 10000th item of Fibonacci sequence’.format(results[1]))

代码执行结果如下:

It takes 0.08275080 seconds to calculate the 10000th item of Fibonacci sequence

It takes 0.08277822 seconds to calculate the 10000th item of Fibonacci sequence

It takes 0.08176851 seconds to calculate the 10000th item of Fibonacci sequence

It takes 0.08178067 seconds to calculate the 10000th item of Fibonacci sequence

It takes 0.08081150 seconds to calculate the 10000th item of Fibonacci sequence

但是这种方法计算的是执行算法的实际时间,有两个明显的缺陷:1) 必须先运行依据算法编制的程序;2) 依赖于特定的计算机、编译器与编程语言等软硬件环境,容易掩盖算法本身的优劣。因此,我们希望找到一个独立于程序或计算机的指标,以用来比较不同实现下的算法。

2.1 大 O O O 表示法

为了摆脱与计算机硬件、软件有关的因素,我们需要一种事前分析估算的方法。可以认为特定算法的“运行工作量”大小取决于问题的规模,或者说,它是问题规模的函数,这时我们就需要量化算法的操作或步骤。一个算法是由控制结构和基本操作构成的,因此可以将算法的执行时间描述成解决问题所需重复执行的基本操作数。需要注意的是,确定合适的基本操作取决于不同的算法。例如在计算斐波那契数列第 n 项时,赋值语句就是一个基本操作,而在计算矩阵乘法时,乘法运算则是其基本操作。

在上一节的 fibo 函数中,整个算法的执行时间与基本操作(赋值)重复执行的次数n 成正比,具体而言是 1 加上 n-2 个赋值语句,如果使用将其定义为函数可以表示为 T ( n ) = n − 1 T(n)=n-1 T(n)=n−1,其中 n n n 为大于 2 的正整数。 n n n 常用于表示问题规模,我们可以使用问题规模 n n n 的某个函数 f ( n ) f(n) f(n) 表示算法中基本操作重复执行的次数,算法的时间量度可以表示如下:

T ( n ) = O ( f ( n ) ) T(n)=O(f(n)) T(n)=O(f(n))

随问题规模 n n n 的增大, T ( n ) T(n) T(n) 函数的某一部分会比其余部分增长得更快,算法间进行比较时这一起部分起决定性作用, T ( n ) T(n) T(n) 增长最快的部分也称为数量级函数。算法执行时间的增长率和 f ( n ) f(n) f(n) 的增长率相同,称作算法的渐近时间复杂度 (asymptotic time complexity),简称时间复杂度。数量级 (order of magnitude) 常被称作大 O O O 记法或大 O O O 表示法。

通过以上分析,我们可以将算法的渐近复杂度规则描述如下:

  • 如果运行时间是一个多项式的和,那么仅保留增长速度最快的项,去掉其他各项;

  • 如果剩下的项是个乘积,那么去掉所有常数。

假设某一算法的基本步骤数为 T ( n ) = 3 n 2 + 50 n + 2000 T(n)=3n^2+50n+2000 T(n)=3n2+50n+2000,当 n n n 很小时 2000 对于函数的影响最大,但是随着 n n n 的增长 n 2 n^2 n2 将逐渐变得更重要,以至于可以忽略其他两项以及 n 2 n^2 n2 的系数 3,因此可以说 T ( n ) T(n) T(n) 的数量级是 n 2 n^2 n2 或写为 O ( n 2 ) O(n^2) O(n2)。

算法的性能有时不仅依赖问题的规模,还取决于算法的输入值,输入令算法运行最慢的情况称为最坏情况,输入令算法运行最快的情况称为最好情况,随机输入的情况下算法的性能介于两种极端情况之间,称为平均情况。

2.2 常见算法复杂度

下表列出了一些常见的大 O O O 表示法实例:

| 复杂度 | 解释 | 示例 |

| — | — | — |

| O ( 1 ) O(1) O(1) | 常数复杂度 | 100, 500, 1, 30, … |

| O ( l o g n ) O(logn) O(logn) | 对数复杂度 | l o g 2 n log_2n log2​n, l o g 10 n log_{10}n log10​n, 2 l o g 2 n 2log_2n 2log2​n, … |

| O ( n ) O(n) O(n) | 线性复杂度 | 8 n + 10 8n+10 8n+10, n n n, 100 n 100n 100n, … |

| O ( n l o g n ) O(nlogn) O(nlogn) | 对数线性复杂度 | 10 n l o g n + 50 10nlogn+50 10nlogn+50, 5 n l o g n + 30 n 5nlogn+30n 5nlogn+30n, … |

| O ( n k ) O(n^k) O(nk) | 多项式复杂度,其中 k k k 为常数 | 4 n 2 − 10 n 4n^2-10n 4n2−10n, 2 n 3 + 10 n 2 2n3+10n2 2n3+10n2, 4 n 2 + 5 n l o g n 4n^2+5nlogn 4n2+5nlogn, … |

| O ( c n ) O(c^n) O(cn) | 指数复杂度,其中 c c c 为常数 | 2 n + 5 n 2 2n+5n2 2n+5n2, 4 n + 10 n l o g n 4^n+10nlogn 4n+10nlogn, … |

常见算法复杂度的增长率比较如下:

增长率比较

2.2.1 常数复杂度

常数复杂度表示,算法的渐进复杂度域输入的规模无关,例如求列表的长度等都属于常数复杂度。常数复杂度和代码中是否包含循环没有必然关系,例如循环打印 100 次 “Hello world”,这与输入规模并没有什么关系,因此其也是属于常数复杂度。

2.2.2 对数复杂度

对数复杂度表示函数的增长速度至少是输入规模的对数,当我们谈论对数复杂度时,我们并不关系对数的底数,这是由于可以使用换底公式,将原来底数的对数乘以一个常数转换为另一个底数:

l o g a n = l o g a b ∗ l o g b n log_an=log_ab*log_bn loga​n=loga​b∗logb​n

其中, a a a 和 b b b 均为常数。例如以下代码,将一个正整数转换为字符串:

def int_to_str(num):

digits = “0123456789”

result = ‘’

if num == 0:

result = ‘0’

else:

while num > 0:

result = digits[num % 10] + result

num = num // 10

return result

上述代码中只包括一个循环,且没有调用其它函数,因此我们只需找出循环迭代次数——在 num 为 0 之前所需的整数除法的次数 l o g 10 n log_{10}n log10​n。因此函数 int_to_str 的复杂度是 O ( l o g n ) O(logn) O(logn)。

2.2.3 线性复杂度

线性复杂度在列表中等序列数据类型总十分常见,因为算法通常需要遍历处理序列中的每一个元素。例如将列表中的每个元素加上常数 10:

def add_constant(list_o):

for i in range(len(list_o)):

list_o[i] += 10

这个函数的复杂度就与列表的长度成线性关系,也就是 O ( n ) O(n) O(n)。

2.2.4 线性对数复杂度

线性对数复杂度是两项的乘积,每个项都依赖于输入的规模,例如将列表中每一项正整数转换为字符串。很多实用算法的复杂度都是对数线性的。

2.2.5 多项式复杂度

多项式复杂度的增长速度是输入规模的 k k k 次幂,其中最常见的是平方复杂度,例如求列表 list_a 和 list_b 的交集:

def intersect(list_a, list_b):

第一部分

temp = []

for i in list_a:

for j in list_b:

if i == j:

temp.append(i)

break

第二部分

result = []

for i in temp:

if i not in result:

result.append(i)

return result

intersect 函数第一部分的复杂度显然是 O ( l e n ( l i s t _ a ) ) ∗ O ( l e n ( l i s t _ b ) ) O(len(list\_a))*O(len(list\_b)) O(len(list_a))∗O(len(list_b)),第二部分代码用于去除第一部分得到结果列表中的重复元素,虽然其中仅包含一个循环语句,但是测试条件 if i not in result 需要检查 result 中的每个元素,因此第二部分的复杂度为 O ( l e n ( t e m p ) ) ∗ O ( l e n ( r e s u l t ) ) O(len(temp))*O(len(result)) O(len(temp))∗O(len(result)),tmp 和 result 的长度取决于 list_a 和 list_b 中长度较小的那个,根据渐进复杂度规则可以将其忽略。最终,intersect 函数的复杂度就是 O ( n 2 ) O(n^2) O(n2)。

2.2.6 指数复杂度

指数复杂度算法的解决时间随输入规模的指数增长。在以下示例中,由于 1 左移 num 位得到 end,因此 end 实际上等于 2 n u m 2^{num} 2num,因此循环中计算了 2 n u m 2^{num} 2num 次加法,时间复杂度为 O ( 2 n ) O(2^{n}) O(2n)。

def calculate(num):

result = 0

end = 1 << num

for i in range(end):

result += i

return result

在这里插入图片描述

感谢每一个认真阅读我文章的人,看着粉丝一路的上涨和关注,礼尚往来总是要有的:

① 2000多本Python电子书(主流和经典的书籍应该都有了)

② Python标准库资料(最全中文版)

③ 项目源码(四五十个有趣且经典的练手项目及源码)

④ Python基础入门、爬虫、web开发、大数据分析方面的视频(适合小白学习)

⑤ Python学习路线图(告别不入流的学习)

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值