Python之树的遍历与排序


这里是一段防爬虫文本,请读者忽略。
本文原创首发于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
    • 堆是一个完全二叉树
    • 每个非叶子结点都要大于或者等于其左右孩子结点的值称为大顶堆
    • 每个非叶子结点都要小于或者等于其左右孩子结点的值称为小顶堆
    • 根结点一定是大顶堆中的最大值,一定是小顶堆中的最小值

  • 大顶堆
    • 完全二叉树的每个非叶子结点都要大于或者等于其左右孩子结点的值称为大顶堆
    • 根结点一定是大顶堆中的最大值

在这里插入图片描述


  • 小顶堆
    • 完全二叉树的每个非叶子结点都要小于或者等于其左右孩子结点的值称为小顶堆
    • 根结点一定是小顶堆中的最小值
      在这里插入图片描述

  1. 构建完全二叉树
  • 待排序数字为
  • 30,20,80,40,50,10,60,70,90
  • 构建一个完全二叉树存放数据,并根据性质5对元素编号,放入顺序的数据结构中
  • 构造一个列表为[0,30,20,80,40,50,10,60,70,90]

  1. 构建大顶堆——核心算法
  • 度数为2的结点A,如果它的左右孩子结点的最大值比它大的,将这个最大值和该结点交换
  • 度数为1的结点A,如果它的左孩子的值大于它,则交换
  • 如果结点A被交换到新的位置,还需要和其孩子结点重复上面的过程

  1. 构建大顶堆——起点结点的选择
  • 从完全二叉树的最后一个结点的双亲结点开始,即最后一层的最右边叶子结点的父结点开始
  • 结点数为n,则起始结点的编号为n//2(性质5)

  1. 构建大顶堆——下一个结点的选择
  • 从起始结点开始向左找其同层结点,到头后再从上一层的最右边结点开始继续向左逐个查找,直至根结点

  1. 大顶堆的目标
  • 确保每个结点的都比左右结点的值大

在这里插入图片描述


  1. 排序
  • 将大顶堆根结点这个最大值和最后一个叶子结点交换,那么最后一个叶子结点就是最大值,将这个叶 子结点排除在待排序结点之外
  • 从根结点开始(新的根结点),重新调整为大顶堆后,重复上一步

在这里插入图片描述


  1. 排序
  • 堆顶和最后一个结点交换,并排除最后一个结点

在这里插入图片描述


在这里插入图片描述


  • 算法实现

  • 代码演示

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基础 | 云计算
  • 如果你有什么疑问,或者是难题。欢迎评论或者私信我。你若留言,我必回复!
  • 虽然我现在还很渺小,但我会做好每一篇内容。谢谢关注!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值