目录
视频简介
伪代码
用介于自然语言和计算机语言之间的文字和符号来描述算法,不属于任何一种语言,可翻译为程序员自身熟悉的语言
大厂面试必备
1.常用的数据结构;
2.常用的算法
算法的时间复杂度
什么是时间复杂度
定义:算法的执行效率、算法的执行时间与算法的输入值之间的关系;一般是看有没有循环结构;好的代码应尽量降低时间复杂度。
表示方法:大O表示法
具体案例
1.时间复杂度O(1),执行时间与输入值 num 无关
def O1(num):
i = num
j = num*2
return i+j
2.时间复杂度O(N),主要看 for 循环
def ON(num):
total = 0
for i in range(num):
total += i
return total
3.时间复杂度O(logN),循环次数是 log2N
def OlogN(num):
i = 1
while (i<num):
i = i*2
return i
4.时间复杂度O(M+N)
def OMN(num1,num2):
total = 0
for i in range(num1):
total += i
for j in range(num2):
total += j
return total
5.时间复杂度O(NlogN)
def ONLogN(num1,num2):
total = 0
j = 0
for i in range(num1):
while (j<num2):
total += i+j
j = j*2
return total
6.时间复杂度O(N²)
def ON2(num):
total = 0
for i in range(num):
for j in range(num):
total += i+j
return total
常见的时间复杂度对比
算法的空间复杂度
什么是空间复杂度
定义:算法存储空间与输入值之间的关系;好的代码应尽量降低空间复杂度。
表示方法:大O表示法
具体案例
左边:O(1),右边:O(N)
判断空间复杂度的方法
1.若变量长度是1(整数、字符串等),空间复杂度是O(1);若变量长度与输入值N相关(列表、数组等),则空间复杂度是O(N);
2.递归结构占用的空间复杂度是O(N)。
常见的空间复杂度对比
O(1) < O(N) < O(N²)
时间复杂度和空间复杂度的关系
一般而言,两者只能二选一,需要进行取舍;
面试时可以根据面试官的偏好,选择时间 / 空间复杂度较低的算法;
工作时:时间 > 空间。
数据结构 - 数组 array
定义
在连续的存储空间中,存储一组相同类型的元素。
数组访问和数组搜索
数组访问:通过索引访问元素
数组搜索:搜索某元素
数组的时间复杂度(重要)
访问:O(1)
原因:可以准确地访问索引对应的内存地址;
搜索:O(N),N是数组的长度
原因:无法得知被搜索元素的位置,只能从头开始寻找;
插入:O(N)
原因:最坏情况下:(1)需要在数组开头插入新元素时,需要将原元素全部往后移一位;(2)内存空间不够时,需要把原元素全部转移到新的内存地址,再插入新元素;
删除:O(N)
原因:最坏情况下,删除首元素时,需要将后面的元素全部往前移一位。
由上述总结:数组适合读,不适合写。
数组常用操作
创建数组
a = []
添加元素
# 尾部添加
a.append(1)
#时间复杂度:
#(1)内存空间足够时:O(1)
#(2)内存空间不足时:O(N)
# 中间插入
# 在索引2的位置添加元素99
a.insert(2,99)
# 时间复杂度:O(N)
访问元素
temp = a[2]
# 时间复杂度:O(1)
更新元素
a[2] = 88
# 时间复杂度:O(1)
删除元素
# 删除元素88
a.remove(88)
# 时间复杂度:O(N)
# 删除索引1的元素
a.pop(1)
# 时间复杂度:O(N)
# 删除最后一个元素
a.pop()
# 时间复杂度:O(1)
获取数组长度
size = len(a)
遍历数组
for i in a:
print(i)
for index,element in enumerate(a):
print('index at',index,'is',element)
for i in range(len(a)):
print('i:',i,'element:',a[i])
# 以上3种方法的时间复杂度都是O(N)
查找元素
# 返回元素2的索引
index = a.index(2)
# 时间复杂度:O(N)
数组排序
# 升序
a.sort()
# 降序
a.sort(reverse = True)
# 时间复杂度:O(NlogN)
数据结构 - 链表 linked list
链表可以在不连续的存储空间中存储数据,充分利用了零碎的内存空间。
链表分为单端链表(常用)和双端链表,单端链表每部分除了数据外,还包括了 next 指针,而双端链表在单端链表基础上增加了 previous 指针。
时间复杂度
访问:O(N)
搜索:O(N)
插入(不包括寻找插入位置):O(1),不用移动插入位置后面的元素
删除(不包括寻找删除位置):O(1),不用移动删除位置后面的元素
特点
写很快,读很慢,读少写多。
链表常用操作
1.创建
linkedlist = deque()
2.添加元素
# 尾部插入:O(1)
linkedlist.append(1)
# 中间插入:O(N)
linkedlist.insert(2,99)
3.访问元素
# O(N)
element = linkedlist[2]
4.搜索元素
# O(N)
index = linkedlist.index(99)
5.更新元素
# O(N)
linkedlist[2] = 88
6.删除元素
# 以下操作都是O(N)
# 删除对应索引的值
del linkedlist[2]
# 删除某值
linkedlist.remove(88)
7.长度
# O(1),直接返回系统中存储的长度值
length = len(linkedlist)