这里是一段防爬虫文本,请读者忽略。
本文原创首发于CSDN,作者IDYS
博客首页:https://blog.csdn.net/weixin_41633902/
本文链接:https://blog.csdn.net/weixin_41633902/article/details/107992741
未经授权,禁止转载!恶意转载,后果自负!尊重原创,远离剽窃!
写在开头的话
- 请记住:实践是掌握知识的最快方法
- 如果你只是怀着看看的态度去快速浏览文章,而不去认认真真的把文章里面讲的任何一个知识点去实践一遍,那么你永远也掌握不了它
- 生命不息,折腾不止!
Python 树的遍历和堆排序
00. 二叉树的遍历
-
遍历:迭代所有元素一遍
-
树的遍历:对树中所有元素不重复地访问一遍,也称作扫描
-
广度优先遍历
- 层序遍历
-
深度优先遍历
- 前序遍历
- 中序遍历
- 后序遍历
-
遍历序列:将树中所有元素遍历一遍后,得到的元素的序列。将层次结构转换成了线性结构
- 层序遍历
- 按照树的层次,从第一层开始,自左向右遍历元素
- 遍历序列
ABCDEFGHI
- 深度优先遍历
- 设树的根结点为
D
,左子树为L
,右子树为R
,且要求L
一定在R
之前,则有下面几种遍历方式: - 前序遍历,也叫先序遍历、也叫先根遍历,
DLR
- 中序遍历,也叫中根遍历,
LDR
- 后序遍历,也叫后根遍历,
LRD
- 设树的根结点为
- 前序遍历
DLR
- 从根结点开始,先左子树后右子树
- 每个子树内部依然是先根结点,再左子树后右子树。递归遍历
- 遍历序列
A BDGH CEIF
- 中序遍历
LDR
- 从根结点的左子树开始遍历,然后是根结点,再右子树
- 每个子树内部,也是先左子树,后根结点,再右子树。递归遍历
- 遍历序列
- 左图 :
GDHB A IECF
- 右图:
GDHB A EICF
- 后序遍历
LRD
- 先左子树,后右子树,再根结点
- 每个子树内部依然是先左子树,后右子树,再根结点。递归遍历
- 遍历序列
GHDB IEFC A
01. 堆排序Heap Sort
- 堆
Heap
- 堆是一个完全二叉树
- 每个非叶子结点都要大于或者等于其左右孩子结点的值称为大顶堆
- 每个非叶子结点都要小于或者等于其左右孩子结点的值称为小顶堆
- 根结点一定是大顶堆中的最大值,一定是小顶堆中的最小值
- 大顶堆
- 完全二叉树的每个非叶子结点都要大于或者等于其左右孩子结点的值称为大顶堆
- 根结点一定是大顶堆中的最大值
- 小顶堆
- 完全二叉树的每个非叶子结点都要小于或者等于其左右孩子结点的值称为小顶堆
- 根结点一定是小顶堆中的最小值
- 构建完全二叉树
- 待排序数字为
30,20,80,40,50,10,60,70,90
- 构建一个完全二叉树存放数据,并根据性质5对元素编号,放入顺序的数据结构中
- 构造一个列表为
[0,30,20,80,40,50,10,60,70,90]
- 构建大顶堆——核心算法
- 度数为
2
的结点A
,如果它的左右孩子结点的最大值比它大的,将这个最大值和该结点交换 - 度数为
1
的结点A,如果它的左孩子的值大于它,则交换 - 如果结点
A
被交换到新的位置,还需要和其孩子结点重复上面的过程
- 构建大顶堆——起点结点的选择
- 从完全二叉树的最后一个结点的双亲结点开始,即最后一层的最右边叶子结点的父结点开始
- 结点数为
n
,则起始结点的编号为n//2
(性质5)
- 构建大顶堆——下一个结点的选择
- 从起始结点开始向左找其同层结点,到头后再从上一层的最右边结点开始继续向左逐个查找,直至根结点
- 大顶堆的目标
- 确保每个结点的都比左右结点的值大
- 排序
- 将大顶堆根结点这个最大值和最后一个叶子结点交换,那么最后一个叶子结点就是最大值,将这个叶 子结点排除在待排序结点之外
- 从根结点开始(新的根结点),重新调整为大顶堆后,重复上一步
- 排序
- 堆顶和最后一个结点交换,并排除最后一个结点
-
算法实现
-
代码演示
import math
def print_tree(array): # 将列表打印为完全二叉树
index = 0
depth = math.ceil(math.log2(len(array)+1))
sep = len(str(max(array))) * " "
for i in range(depth):
offset = 2 ** i
print((2**(depth-i -1)-1) * sep, end="")
line = array[index:index+offset]
for j, x in enumerate(line):
print("{:>{}}".format(x, len(str(max(array)))), end="")
interval = 0 if i == 0 else 2 ** (depth - i) - 1
if j < len(line) -1:
print(sep * interval, end="")
index += offset
print()
def heap_core(i, array: list): # 构建大顶堆,核心算法
array.insert(0, 0)
total = len(array) - 1
while 2 * i <= total:
max_index = 2*i
if total > 2*i and array[2*i + 1] > array[2*i]:
max_index = 2*i +1
if array[i] < array[max_index]:
array[i], array[max_index] = array[max_index], array[i]
i = max_index
else:
break
array.pop(0)
return array
def heap_program(array: list): # 构建大顶堆程序
total = len(array)
for i in range(total // 2,0, -1):
heap_core(i, array)
return array
def head_sort(array: list): # 排序
total = len(array) - 1
i = 0
heap_program(array)
while total > 0:
array[total], array[0] = array[0], array[total]
total -= 1
i += 1
array_new = heap_program(array[:-i])
array = array_new + array[-i:]
return array
if __name__ == "__main__":
array = [12, 307, 17, 19, 20, 1, 901, 231, 45, 9, 9010]
print_tree(array) # 将列表打印为完全二叉树
heap_program(array) # 构建大顶堆
print("***************************************************************")
print_tree(array) # 打印完全二叉树
print("****************************************************************")
print(head_sort(array)) # 排序后打印
- 运行结果
12
307 17
19 20 1 901
231 45 9 9010
***************************************************************
9010
307 901
231 20 1 17
19 45 9 12
****************************************************************
[1, 9, 12, 17, 19, 20, 45, 231, 307, 901, 9010]
02. 总结
-
是利用堆性质的一种选择排序,在堆顶选出最大值或者最小值
- 时间复杂度
- 堆排序的时间复杂度为
O(nlogn)
- 由于堆排序对原始记录的排序状态并不敏感,因此它无论是最好、最坏和平均时间复杂度均为
O(nlogn)
-
空间复杂度
- 只是使用了一个交换用的空间,空间复杂度就是O(1)
-
稳定性
- 不稳定的排序算法
写在最后的话:
- 无论每个知识点的难易程度如何,我都会尽力将它描绘得足够细致
- 欢迎关注我的CSDN博客,IDYS’BLOG
- 持续更新内容:
linux基础 | 数据通信(路由交换,WLAN) | Python基础 | 云计算 - 如果你有什么疑问,或者是难题。欢迎评论或者私信我。你若留言,我必回复!
- 虽然我现在还很渺小,但我会做好每一篇内容。谢谢关注!