其他分享
首页 > 其他分享> > 堆排序——原理详解+代码实践

堆排序——原理详解+代码实践

作者:互联网

预备知识

定义

堆(英语:heap),是计算机科学中的一种特别的完全二叉树,它满足以下性质:

分类

根据父节点和儿子节点之间的大小关系,可分为小根堆大根堆(如下图):

基本操作

操作描述时间复杂度
build建立一个空堆O(n)
insert向堆中插入一个元素O(logn)
get获取堆顶元素O(1)
delete删除堆顶元素O(logn)
heapify使删除堆顶元素的堆再次成为堆O(logn)

insert操作

  1. 将新元素插入到堆的尾部
  2. 将新元素heapifyup,即依次向上调整整个堆的结构
    delete操作
  3. 将堆尾元素替换掉堆顶元素(实现所谓删除)
  4. 对堆顶元素heapifydown,即依次向下调整整个堆的结构

用途

建立优先队列、找最值、堆排序等。

算法详解

首先解决一个问题,即为什么我们要进行堆排序?在正常的堆中,数据只是在父子关系的层面上有序,在小跟堆中每个孩子节点都大于它的父节点,在大跟堆中每个孩子节点都小于它的父节点。这种粒度的有序太粗糙,为了达到整体的有序,因此需要排序。

算法过程

以构造一个降序序列为例:
构造降序序列,初始化对应构建小根堆(反之,大根堆)。
一、 将待排序序列构成一个小根堆

  1. 设初始数组为[6,1,2,3,0,4,5,7]
  2. 按照完全二叉树,将数字依次填入
  3. 找到最下面的非叶子节点(第len(nums)//2-1个节点),开始依次调整,即根据小根堆的性质,把每个节点看做根节点,做一次heapifydown,让小元素上浮,大元素下沉
  4. 从下至上(数组的排列上),从右往左(树的结构上),从最底非叶子节点到根节点,对每个节点做这样的调整
    建立小根堆
    二、 将堆顶元素和末尾元素交换此时末尾元素就是最小值
    三、将剩余n-1个元素重新构造成一个小根堆,得到n个元素的第二小值
    首末元素互换以调整堆
    四、 反复执行二、三步,得到降序有序序列
    降序有序序列

时间复杂度分析

O(nlogn). 其中,构造小根堆时间复杂度为O(n),每个元素下沉的时间复杂度为O(logn),一共有n-1个元素需要下沉,故总共消耗的时间为O(n+(n-1)*logn),即O(nlogn).

空间复杂度分析

O(1). 堆本身所占用的数组空间n不计算在内(空间复杂度仅计算辅助变量所开辟的空间大小),对于原地排序而言,空间复杂度为O(1).

稳定性分析

不稳定。

代码实现

def heapSort(nums):
    n = len(nums)
    # 初始建堆,从最后一个非叶子节点往上,依次做heapifyDown
    for i in range(n//2 - 1, -1, -1):
        heapifyDown(nums, i, n)
    # 堆排序(调整堆,使有序)
    for i in range(n-1, 0, -1):
        nums[0], nums[i] = nums[i], nums[0]
        heapifyDown(nums, 0, i)

def heapifyDown(nums, i, n):
    lt, rt = 2*i+1, 2*i+2
    smallest = i
    if lt < n and nums[lt] < nums[smallest]:
        smallest = lt
    if rt < n and nums[rt] < nums[smallest]:
        smallest = rt
    if smallest != i:
        nums[smallest], nums[i] = nums[i], nums[smallest]
        heapifyDown(nums, smallest, n)

标签:smallest,nums,代码,元素,堆排序,详解,复杂度,小根堆,节点
来源: https://blog.csdn.net/weixin_42365868/article/details/114112397