python算法 —— 堆排序

堆排序

树与二叉树

  • 树是一种数据结构      比如:目录结构

  • 树是一种可以递归定义的数据结构

  • 树是由n个节点组成的集合:

    • 如果n=0,那这是一棵空树;
    • 如果n>0,那么存在1个节点作为树的根节点,其他节点可以分为m个集合,每个集合本身又是一棵树。

树结构的一些基本定义

示意图
在这里插入图片描述

  • 结点的度:结点拥有的子树的数目。eg:结点 A 的度为3
  • 树的度:树种各结点度的最大值。eg:树的度为3
  • 根节点:最上面那个节点 A
  • 叶子结点:度为 0 的结点。eg:E、F、C、G 为叶子结点
  • 孩子结点:一个结点的子树的根节点。eg:B、C、D 为 A 的子结点
  • 父结点:B 为 A 的子结点,那么 A 为 B 的父结点
  • 兄弟结点:一个双亲结点结点的孩子互为兄弟结点。eg:B、C、D 为兄弟结点
  • 结点的层次:根节点为第一层,子结点为第二层,依次向下递推…eg:E、F、G 的层次均为 3
  • 树的深度:树种结点的最大深度。eg:该树的深度为 3
  • 森林:m 棵互不相交的树称为森林

二叉树

二叉树的定义

二叉树是一种特殊的树:它或者为空,或者由一个根节点加上根节点的左子树和右子树组成,这里要求左子树和右子树互不相交,且同为二叉树,很显然,这个定义是递归形式的。

  • 二叉树:度不超过2的树
  • 每个节点最多有两个孩子节点
  • 两个孩子节点被区分为左孩子节点和右孩子节点

满二叉树与完全二叉树

满二叉树: 如果一棵二叉树的任意一个结点或者是叶子结点,或者有两棵子树,同时叶子结点都集中在二叉树的最下面一层上,这样的二叉树称为满二叉树

完全二叉树: 若二叉树中最多只有最下面两层结点的度小于 2 ,并且最下面一层的结点(叶子结点)都依次排列在该层最左边的位置上,具有这样结构特点的树结构称为完全二叉树。
在这里插入图片描述
在这里插入图片描述

完全二叉树的特性

当前节点:i (在数组中的位置)
父节点的位置 = (i-1) // 2
左边子节点(左孩子)位置 = 2i + 1
右边子节点(右孩子)位置 = 2i + 2

堆的定义(什么是堆?)

  • 堆:一种特殊的完全二叉树结构

    • 大根堆:一棵完全二叉树,满足任一节点都比其孩子节点大
    • 小根堆: 一棵完全二叉树,满足任一节点都比其孩子节点小

堆排序的过程

  • 1.建立堆
  • 2.得到堆顶元素,为最大元素
  • 3.去掉堆顶,将堆最后一个元素放到堆顶,此时可通过一次调整重新使堆有序
  • 4.堆顶元素为第二大元素
  • 5.重复步骤3,直到堆变空

构建堆:从倒数第二层开始,依次往上变

算法描述

  • 初始时把要排序的数的序列看作是一棵顺序存储的二叉树,调整它们的存储序,使之成为一个 堆,这时堆的根节点的数最大
  • 然后将根节点与堆的最后一个节点交换。然后对前面(n-1)个数重新调整使之成为堆
  • 依此类推,直到只有两个节点的堆,并对 它们作交换,最后得到有n个节点的有序序列

堆排序动图演示


代码实现

代码1>

def sift(arr, low, high):
    """向下调整
    :param arr: 列表
    :param low: 堆的根节点位置
    :param high: 堆的最后一个元素的位置
    """
    i = low   # i最开始指向根节点
    j = 2 * i + 1  # j最开始是左孩子
    tmp = arr[low]  # 把堆顶存起来
    while j <= high:  # 只要j位置有数
        if j + 1 <= high and arr[j + 1] > arr[j]:  # 如果右孩子有并且比左孩子大
            j = j + 1  # 让j指向右孩子
        if arr[j] > tmp:
            arr[i] = arr[j]
            i = j  # 往下看一层
            j = 2 * i + 1
        else:
            break
    arr[i] = tmp  # 把tmp放到叶子节点上


def heap_sort(arr):
    """推排序"""
    n = len(arr)
    # 1.建堆
    for i in range((n - 2) // 2, -1, -1):
        # i 表示建队的时候调整部分的根的下标
        sift(arr, i, n - 1)

    # 2.挨个出数
    for i in range(n - 1, -1, -1):  # i 指向当前堆的最后一个元素

        # 交换 i 和堆顶位置的元素
        tmp = arr[i]
        arr[i] = arr[0]
        arr[0] = tmp

        sift(arr, 0, i - 1)  # i-1是新的high


if __name__ == "__main__":
    numbers = [2, 9, 7, 6, 8, 10, 3, 5, 2, 4, 0, 1, 13]
    heap_sort(numbers)
    print(numbers)

代码 2>

def heapify(tree, n, i):
    """
    维护堆的性质(向下调整)
    :param tree: 完全二叉树数组(存储堆的数组)
    :param n: 树中的节点个数,即数组的长度
    :param i: 待维护节点的下标,即对哪一个节点做heapify的操作
    :return:
    """
    if i >= n:
        return
    left = 2 * i + 1  # 左孩子节点
    right = 2 * i + 2  # 右孩子节点
    max_index = i
    if left < n and tree[left] > tree[max_index]:  # left < n:保证左孩子节点不出界
        max_index = left
    if right < n and tree[right] > tree[max_index]:
        max_index = right
    if max_index != i:
        tree[i], tree[max_index] = tree[max_index], tree[i]
        heapify(tree, n, max_index)


def build_heap(tree, n):
    last_node = n - 1
    parent = (last_node - 1) // 2
    for i in range(parent, -1, -1):
        heapify(tree, n, i)


def heap_sort(tree, n):
    build_heap(tree, n)
    for i in range(n - 1, -1, -1):
        tree[0], tree[i] = tree[i], tree[0]
        heapify(tree, i, 0)  # i 表示树中节点的个数,每交换一次,树中节点个数就减少一个
        # 即:树中节点个数最开始为n,当堆顶元素和最后位置的元素交换后,树中节点个数=n-1,所以每次循环树中节点个数是i


if __name__ == "__main__":
    numbers = [10, 2, 5, 8, 9, 6, 3, 1, 0, 25, 41, 26]
    heap_sort(numbers, len(numbers))
    print(numbers)

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

*Heygirl

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值