树
1. 树 是一种数据结构,比如目录结构
2. 数 是一种可以递归定义的数据结构
3. 树 是由n个节点组成的集合:
- 如果n=0,那这是一棵空树
- 如果n>0,那存在1个节点作为树的根节点,其他节点可以分为m个集合,每个集合本身又是一棵树。
根节点:A就是根节点
叶子节点: 不能分叉的节点(叶子)
树的深度(高度):4
节点E的度:2
树的度: 树中节点最多的度,6
孩子节点/父节点: E 为 I 的父节点,I 为 E 的节点
子树: 树的某个分支
二叉树
度不超过 2 的树,每个节点最多有两个孩子节点。两个孩子节点被区分为左孩子结点 和 右孩子节点。
堆是一个特殊的二叉树
二叉树的存储方式(表示方式):怎么实现一个二叉树
- 链式存储方式(数据结构部分讲解)
- 顺序存储方式(堆排序中)
一. 堆排序:
1. 堆: 一种特殊的完全二叉树结构(全满但最好一排后边可以少)。
- 大堆根:一棵完全二叉树,满足任一节点都比其他孩子节点大。
- 小堆根:一棵完全二叉树,满足任一节点都比其孩子节点小。
可以将其形象的看作 市长、县长、村长、村民 。以下以大根堆为例。
父节点找子节点和子节点找父节点:
知道孩子节点下标为n,则其父结点下标为:(n-1)//2。
2. 堆的向下调整----构建堆的基础:
假设:根节点的左右子树都是堆,但根节点不满足根的性质,那么可以通过一次向下的调整将其变成一个堆...... 向下调整顺序如下:
---->
--->
---->
当根节点的左右子树都是堆时,但自身不满足堆的性质. 可以通过一次向下调整来将其变换成一个堆
3. 堆构建好之后如何排序: 挨个出数
--拿出堆顶9--
--向下调整--
--再拿出堆顶----向下调整--
--再拿出堆顶,将2放到堆顶
改进:
堆 相当于一个列表, 拿出来的堆顶元素放在另一个列表里很废内存, 秉持着能省则省的原则, 我们不建议再创建一个列表去放堆顶元素。 我们可以将 堆顶元素和最后的元素互换位置,并且将堆最后的位置向前移动一位,如图:
------>
说明:3原本在最后一个元素,让3和9互换,然后将堆的位置下标变到4的位置下标(下标减1)。
4.如何构造堆
要建造堆, 首先要让下级先有序, 因此需要从最下面的根节点开始, 即最后一个 非叶子节点开始. "农村包围城市战略"
先对最后一个非叶子节点做 向下调整, 让他变成堆 ; 然后依次将前面的每个非叶子节点 通过向下调整 变成堆.
5.堆排序过程
6. 代码实现过程
向下调整函数:复杂度:O(log n)
# 向下调整函数
def sift(li,low,high): #low表示二叉树的顶;high的作用是:防止j越界
'''
堆排序中的向下调整函数
:param li:列表
:param low: 堆的根节点位置
:param high:堆的最后一个元素的位置,唯一的作用是防止 j 越界
:return:
'''
i=low
j=2*i+1 # j现在指的是左孩子结点
tmp=li[low]
while j <= high: # 表示 j 不越界
if j+1 <= high and li[j+1] > li[j]: # 右孩子结点存在且更大
j=j+1 # j指向右孩子结点
if tmp < li[j]:
li[i]=li[j]
i=j # i 指向下一层
j=2*i+1
else: # 表示tmp>li[j]
li[i]=tmp
break
else: # 如果i到达叶子节点,最后一层,j已经出界
li[i]=tmp
构建堆和排序(挨个出数):复杂度:O(nlog n)
def heap_sort(li):
# 构造堆:农村包围城市
n=len(li)
for i in range((n-2)//2,-1,-1): # i表示建堆的时候调整部分的根节点下标 #将这个小分支变成堆:进行向下调整
sift(li,i,n-1) # j唯一的作用就是防止j越界,high为最后一个元素时,也能满足该条件
# 堆构造完成
print(li)
# 接下来就是排序:挨个出数
for i in range(n-1,-1,-1): # i指向堆的最后一个元素
li[0],li[i]=li[i],li[0] # 将最后一个元素和堆顶元素互换
sift(li,0,i-1) # 将没排序的数据 做向下调整,i-1是新的high
最终堆排序的时间复杂度:O(nlog n)