一、常用的数据结构
1、数组
数组在内存中是连续存储,通过起始位置和偏移量来对其中每一个元素进行访问;
优点:随机访问很好。资源使用少
缺点:插入或者删除元素比较麻烦。
第一个元素:起始地址+0偏移量
第二个元素:起始位置+1偏移量
第i个元素: 起始位置+(i-1)*偏移量
时间复杂度 :数组的随机访问元素时间复杂度O(1):常数时间复杂度
线性存储:每个元素都有一个前驱元素,一个后续元素
2、链表
链表是线性存储的数据结构,跟数组不同在于,链表不是连续的,链表中的每一个点被称为【节点】,每个节点都包含两个部分:数据域和指针域
其中数据域存储的是元素,指针域存储的是下一个节点的地址
链表分为:单向链表、双向链表(有三个区域,中间的是数据域,前驱元素的地址,后续元素的地址)
优点:链表插入或者删除元素效率高,只需要修改指针域即可
缺点:链表的随机访问性不好,资源占用的多。
如果既要有随机访问性,又删除和添加操作快-----在链表和数组中选一个的话只能选数组,理由如下:
1、节省空间
2、添加和删除操作,链表本身也需要遍历(访问),找到对应的元素,所以可能也会浪费时间
3、堆栈(栈)
受限的线性表:只能在线性表的一端添加或者删除元素,不能在中间添加或者删除元素。
栈顶,栈底
出栈顺序:无论是添加元素,删除元素,都是从栈顶操作。
后进先出,栈顶元素先出 LIFO last in first out
应用场合: 函数调用,异常的传播和抛出
4、队列
受限的线性表,只能在队尾添加元素,只能在队头删除元素,不能在中间添加或者删除元素
队头(表头),队尾(表的最后一个元素)
出队列的顺序:先进先出,队头元素先出 FIFO first in first out
双端队列:同时实现队列和堆栈的特性
5、树
树是由节点集和连接每对节点的有向边集组成,结构有:根、父节点、子节点、叶子节点(没有子节点)
二叉树:任何一个节点的孩子数不超过两个,分别称为左子树和右子树
满二叉树:树的深度m,2^m-1个节点,(除了叶子节点以外,其余都有两个孩子)
完全二叉树:当所有节点的编号跟满二叉树一致,这种二叉树是完全二叉树
二叉树的遍历分为三种,都是针对根节点来说的
先序遍历:先根、左、右
中序遍历:左,根,右
后序遍历:左,右,根
6、哈希表
哈希映射
数学上函数y=f(x) ---- y=x+1
将一组关键字映射到有限的地址集合上,这种就叫做哈希散列表
关键字映射到相应地址的函数:哈希函数
python中字典使用的是哈希表存储结构
二、算法的衡量标准
时间复杂度:以最坏时间复杂度为准
常用的时间复杂度
O(1)< O(logn)<O(n)<O(nlogn)<O(n^2)
O(1)复杂度【注意没有O(2)、O(3)】指:
x=1
y=2
O(logn)复杂度指:
while n!=0:
print(n)
n=n//2
O(n)复杂度指:
n=10
for i in range(n):
print("x")
O(n^2)复杂度指:
for i in range(n):
# for j in range(n):
for j in range(i):
print("x")
空间复杂度:用空间为代价,换取时间
三、查找算法的实现
1、顺序查找
前到后,顺序查找跟关键字元素相符合的元素
def search(li,key):
for index,i in enumerate(li):
if i==key:
return index
return -1
li=[1,2,5,6,77,88]
print(search(li,6))
时间复杂度是O(n)
2.折半查找
前提条件:列表中的元素必须是有序
选择中间的元素进行比较,每次排除一半的元素
def findMiddle(key,li):
startIndex = 0
endIndex = len(li)-1
while startIndex<= endIndex:
middleIndex = (startIndex + endIndex) // 2
if key == li[middleIndex]:
return middleIndex
elif key < li[middleIndex]:
endIndex = middleIndex-1
elif key>li[middleIndex]:
startIndex = middleIndex+1
return -1
时间复杂度: O(logn)
折半查找是不是一定好于顺序查找?
折半查找需要先进行排序,当重复调用查找的方法的时候,适合折半查找。进行排序之后,一劳永逸的进行查找。