Python 中文数据结构和算法教程
Python 讲解笔记与代码 - Github
视频教程:极客时间-算法通关,参考理论讲解
视频教程:Python数据结构与算法教程
利用class实现数据结构
Abstract Data Type, ADT:抽象数据类型
data:容器
method
注意事项
LinkedList
__len__
, append, appendleft, iter_node, __iter__
, remove, find, popleft, clear, reverse代码示例:linked_list_adt.py
LinkedList.reverse()
LinkedList
代码实现:略
Stack
代码实现:stack_adt.py
list
实现思考题:利用双端队列实现
collections.deque
实现collections.deque
python内置的 双端队列
class Queue:
"""队尾进,队首出"""
def __init__(self):
self._queue = []
def add(self, value):
self._queue.append(value)
def out(self):
return self._queue.pop(0)
参考:尚硅谷-数组模拟队列
使用head和tail分别记录 队首(队首前一个) 和 队尾。
数据存入队列是add_queue:
参考:尚硅谷-数组模拟循环队列
(tail+1)%maxSize == head
。tail==head
为空。(tail + maxSize - head) % maxSize
。字典和集合一般是基于哈希表或二叉树实现。
HashMap/HashSet | TreeMap/TreeSet | |
---|---|---|
查询 | O(1) | O(logN) |
排序 | 无序 | 相对有序 |
递归的本质是循环,通过函数体进行的循环。
F(n-1) + F(n-2)
O(2^n)
,虽然层级近似2^6
。F(3)
有三次。递归是基于 调用栈 实现
也是一种递归
解决方案
仍然存在子问题重复操作的问题
冒泡、选择和插入算法的复杂度均为O(N^2)
basic_sort.py
# 不断把大元素挤到右侧
第1轮:[46, 39, 12, 73, 33, 99, 6, 51, 53, 38]
第2轮:[39, 12, 46, 33, 73, 6, 51, 53, 38, 99]
第3轮:[12, 39, 33, 46, 6, 51, 53, 38, 73, 99]
第4轮:[12, 33, 39, 6, 46, 51, 38, 53, 73, 99]
第5轮:[12, 33, 6, 39, 46, 38, 51, 53, 73, 99]
第6轮:[12, 6, 33, 39, 38, 46, 51, 53, 73, 99]
第7轮:[6, 12, 33, 38, 39, 46, 51, 53, 73, 99]
第8轮:[6, 12, 33, 38, 39, 46, 51, 53, 73, 99]
第9轮:[6, 12, 33, 38, 39, 46, 51, 53, 73, 99]
参考视频:尚硅谷-golang-冒泡排序
# 不断找小元素插入左侧
第1轮:[6, 39, 12, 73, 33, 99, 46, 51, 53, 38]
第2轮:[6, 12, 39, 73, 33, 99, 46, 51, 53, 38]
第3轮:[6, 12, 33, 73, 39, 99, 46, 51, 53, 38]
第4轮:[6, 12, 33, 39, 73, 99, 46, 51, 53, 38]
第5轮:[6, 12, 33, 39, 46, 99, 73, 51, 53, 38]
第6轮:[6, 12, 33, 39, 46, 51, 73, 99, 53, 38]
第7轮:[6, 12, 33, 39, 46, 51, 53, 99, 73, 38]
第8轮:[6, 12, 33, 39, 46, 51, 53, 73, 99, 38]
第9轮:[6, 12, 33, 39, 46, 51, 53, 73, 99, 38]
参考视频:尚硅谷-golang-选择排序
问题背景:同样10个身高不等的小朋友,按身高排队
思路分析:
排序过程:
# 不断把新元素放到已经有序的数组中
第1轮:[39, 46, 12, 73, 33, 99, 6, 51, 53, 38]
第2轮:[12, 39, 46, 73, 33, 99, 6, 51, 53, 38]
第3轮:[12, 39, 46, 73, 33, 99, 6, 51, 53, 38]
第4轮:[12, 33, 39, 46, 73, 99, 6, 51, 53, 38]
第5轮:[12, 33, 39, 46, 73, 99, 6, 51, 53, 38]
第6轮:[6, 12, 33, 39, 46, 73, 99, 51, 53, 38]
第7轮:[6, 12, 33, 39, 46, 51, 73, 99, 53, 38]
第8轮:[6, 12, 33, 39, 46, 51, 53, 73, 99, 38]
第9轮:[6, 12, 33, 38, 39, 46, 51, 53, 73, 99]
参考视频:尚硅谷-插入排序
O(n logn)
(不忽略常数项为:O(cn logn + cn)
)merge_sort.py
[46, 39, 12, 73, 33, 99, 6, 51, 53, 38]
↓
[46, 39, 12, 73, 33] [99, 6, 51, 53, 38]
↓ ↓
[46, 39] [12, 73, 33] [99, 6] [51, 53, 38]
↓ ↓ ↓ ↓
[46] [39] [12] [73, 33] [99] [6] [51] [53, 38]
↓ ↓
[73] [33] [53] [38]
A = [1, 3, 5, 7, 9, 11]
B = [0, 2, 8, 9, 11, 15, 16, 17]
# 如何得到有序的 new_seq ?
[46] [39] [12] [73] [33] [99] [6] [51] [53] [38]
↓ ↓ ↓ ↓ ↓ ↓
[39, 46] ↓ [33, 73] [6, 99] ↓ [38, 53]
↓ ↓ ↓ ↓
↓ [12, 33, 73] ↓ [38, 51, 53]
↓ ↓
[12, 33, 39, 46, 73] [6, 38, 51, 53, 99]
↓
[6, 12, 33, 38, 39, 46, 51, 53, 73, 99]
代码示例:quick_sort.py
时间复杂度:O(n*logn)
很多程序语言的内置排序都有它的影子。
快排也是一种分而治之(divide and conquer)的策略
快速排序的基本步骤:
快速排序过程详解
# 分治
[46, 39, 12, 73, 33, 99, 6, 51, 53, 38]
⬇️
[39, 12, 33, 6, 38] 46 [73, 99, 51, 53]
⬇️ ⬇️
[12, 33, 6, 38] 39 [] [51, 53] 73 [99]
⬇️ ⬇️
[6] 12 [33, 38] [] 51 [53]
⬇️
[] 33 [38]
# 然后,合并
缺陷:
inplace原地排序,来实现parition操作
代码示例:优化后的快排(原地排序,partition只遍历一遍数组)
参考视频:尚硅谷-快速排序
树状结构是对 链表的进化。
树(Tree)
Binary Tree
工程中常用二叉搜索树
二叉搜索树(Binary Search Tree),又称二叉查找树、有序二叉树(Ordered Binary Tree)。
排序二叉树(Sorted Binary Tree)是指一棵空树或者具有下列性质的二叉树。
查找数据的复杂度为:O(logN)
性能优化:当二叉搜索树的性能退化时,可以打乱并重构为新的二叉搜索树。
进化数据结构:前三种最差情况也为O(logN)
三种遍历方式:
实际工程使用:
key
在二叉搜索树中查找删除节点有三种情况:
有一个子节点:删除有一个孩子的节点时,我们拿掉需要删除的节点,之后把它的父亲指向它的孩子就行,因为根据 BST 左子树都小于节点,右子树都大于节点的特性,删除它之后这个条件依旧满足。
有两个子节点:
代码实现:略
O(nlogn)
堆是一种完全二叉树,有最大堆和最小堆两种。
最大堆: 对于每个非叶子节点 V,V 的值都比它的两个孩子大,称为 最大堆特性(heap order property) 最大堆里的根总是存储最大值,最小的值存储在叶节点。
parent = int((i-1) / 2) # 取整
left = 2 * i + 1
right = 2 * i + 2
array_adt.py
实现number_list = [0, 1, 2, 3, 4, 5, 6, 7]
def linear_search(value, iterable):
for index, val in enumerate(iterable):
if val == value:
return index
return -1
前提条件:
时间复杂度:O(logN)
缺点:必须是有序数组