python-排序算法(三)、堆排序

1、树

1)树的定义

树是一种数据结构

树是一种可以递归定义的数据结构
树是由n个节点组成的集合

         如果n=0,那么这是一棵空树

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

2)根节点、叶子节点

        如下A为根节点

        B、C、H、I、P、Q、K、L、M、N为叶子节点(没有孩子)

3)树的深度(高度)

        如下该树的深度为4

4)树的度

        度:节点中孩子节点的个数(如A节点的度为6,F节点的度为3)

        树的度:整个树中节点的最大的度(该树的度为6)

5)孩子节点/父节点

        A是B-G的父节点

        B-G是A的孩子节点

        

2、二叉树

1)定义:

不超过2的树

每个节点最多有两个孩子节点

两个孩子节点被区分为左孩子节点右孩子节点

2)满二叉树:

一个二叉树,如果每一层的节点树都达到最大值,则这个二叉树就是满二叉树

3)完全二叉树:

叶子节点只能出现在最下层和次下层,并且最下面一层的节点都集中在该层最左边的若干位置的二叉树

4)对于二叉树父亲节点找孩子节点

将下图二叉树变为列表

i为父节点的下标

此时:

        左孩子下标=2i+1

        右孩子下标=2i+2

如根节点9找左右孩子节点,9的下标为0,则左孩子下标为2*0+1=1,为8, 右孩子的下标为2*0+2=2 ,为7

5)对于二叉树孩子节点找父亲节点

n为孩子节点的下标

        父亲节点下标=(n-1)//2  (//整除向下取整)

如0、1节点找父亲节点7

此时0、1节点的下标为5、6。那么(5-1)// 2=2,(6-1)//2=2。则可得父亲节点的下标为2也就是7

 3、堆

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

大根堆:一颗完全的二叉树,满足任意节点都比其他孩子节点大

小根堆:一颗完全的二叉树,满足任意节点都比其他孩子节点小

 

  4、堆的向下调整

如果根节点的左右子树都是堆,但整棵树不是堆,那么我们可以通过一次向下的调整来将其变换成一个堆。如下图就是一个二叉树,但不是一个堆

列表:li = [2, 9, 7, 8, 5, 0, 1, 6, 4, 3] 

通过向下调整可以将其变成大根堆,如下动态图:

1、根节点比两个孩子节点都小,且第二层的节点又大于它的两个孩子节点,此时该二叉树既不是大根堆也不是小根堆

2、我们可以通过向下调整将该二叉树变成一个大根堆

 代码实现(结合上面的动图看代码)

def shit(li, low, height):
    """
    :param li: 列表
    :param low: 列表的第一个下标(堆顶元素的位置)
    :param height: 列表的最后一个下标(堆顶的最后一个元素的位置)
    :return:
    """
    i = low  # 当前i指向2的位置(i用于标识当前树或子树的堆顶)
    j = i * 2 + 1  # 当前j指向i的左孩子9的位置(j用于指向i的左孩子和右孩子,谁大指向谁)
    tmp = li[low]  # 暂存堆顶tmp=2
    while j <= height:  # 如果j>height证明i到了最后一层,此时退出循环(1<=9)
        # 判断当前i的左孩子和右孩子的大小
        if j + 1 <= height and li[j + 1] > li[j]:  # 判断右孩子是否存在且比左孩子大(2<=9 and 7>9)
            j = j + 1  # 当前j指向右孩子
        # 判断当前堆顶的右孩子是否大于堆顶
        if li[j] > tmp: (9>2)
            li[i] = li[j]  # 将右孩子至于堆顶(9)
            i = j  # 现在将i指向j的位置(指向原来9的位置)
            j = i * 2 + 1  # 将j指向i的左孩子(j指向8)---继续循环
        else:  # 证明当前堆顶比它的右孩子或者左孩子大,则退出循环
            break
    # 当循环结束时,将tmp至于当前i的位置
    li[i] = tmp

li = [2, 9, 7, 8, 5, 0, 1, 6, 4, 3]
shit(li, 0, len(li) - 1)
print(li)

# 结果
[9, 8, 7, 6, 5, 0, 1, 2, 4, 3]

5、堆排序的过程

        1)构造堆

1、构造堆相比于向下调整来说,它是一个逆序的过程。当我们对一个列表进行堆排序时,此时该列表并不是堆,所以我们需要通过构造堆的方法进行构造大根堆或者小根堆,构造堆其实就是对二叉树的每一个子树进行向下调整。

2、如图

先从最后一个子树(3:5)开始进行向下调整,然后依次对(9:2-4)(1:0-7)(8:9-3)(6:8-1)进行向下调整

li = [6, 8, 1, 9, 3, 0, 7, 2, 4, 5]

 代码:

def heap_sort(li):
    n = len(li) - 1  # 列表的长度
    # 倒序遍历所有的父亲节点,步长 -1, 依次进行向下调整
    for i in range((n - 1) // 2, -1, -1):  # 最后一个叶子节点的位置为(n),它的父亲节点为(n-1)//2
        # 对当前的的子树进行向下调整
        shit(li, i, n)
    # 构建堆完成


li = [6, 8, 1, 9, 3, 0, 7, 2, 4, 5]
heap_sort(li)
print(li)

# 结果
[9, 8, 7, 6, 5, 0, 1, 2, 4, 3]

[9, 8, 7, 6, 5, 0, 1, 2, 4, 3]建堆后为大根堆

       2)挨个出数进行排序

第一次得到堆顶元素,为最大元素,然后去掉堆顶,将堆最后一个元素放到堆顶,此时可以通过一次向下调整重新使堆有序,然后此时堆顶元素为第二大元素,重复步骤,直到堆变空

def heap_sort(li):
    n = len(li) - 1  # 列表的长度
    # 1、构建堆
    # 倒序遍历所有的父亲节点,步长 -1, 依次进行向下调整
    for i in range((n - 1) // 2, -1, -1):  # 最后一个叶子节点的位置为(n),它的父亲节点为(n-1)//2
        # 对当前的的子树进行向下调整
        shit(li, i, n)
    # 2、挨个出数排序
    for i in range(n, -1, -1):  # 倒序循环整个堆
        li[0], li[i] = li[i], li[0]  # 每次取出的堆顶元素和都和当前堆尾元素交换
        shit(li, 0, i - 1)  # 当前i位置就是最大的数,再对堆尾为i-1的堆进行向下调整


li = [6, 8, 1, 9, 3, 0, 7, 2, 4, 5]
heap_sort(li)
print(li)

# 结果
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

堆排序的时间复杂度

        O(nlogn)

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值