Heapsort--算法导论版

348 阅读1分钟

0. 代码

#!/usr/bin/env python3
# -*- coding: utf-8 -*-

from sys import maxsize as MAX

def parent(i):
    if i:
        return (i // 2 - 1)
    return MAX

def left(i):
    return (2 * i + 1)

def right(i):
    return (2 * (i + 1))

def max_heapify(arr, i, heapsize = 1):
    l = left(i)
    r = right(i)
    largest = len(arr) # 取一个怎么都取不到的值
    if l <= heapsize and arr[l] > arr[i]:
        largest = l
    else:
        largest = i
    if r <= heapsize and arr[r] > arr[largest]:
        largest = r
    if largest != i:
        arr[i], arr[largest] = arr[largest], arr[i]
        max_heapify(arr, largest, heapsize)
    return

def build_max_heap(arr):
    heapsize = len(arr) - 1
    for i in range(len(arr) // 2, -1, -1):
        max_heapify(arr, i, heapsize)
    return heapsize

def heapsort(arr):
    heapsize = build_max_heap(arr)
    for i in range(len(arr) - 1, 0, -1):
        arr[0], arr[i] = arr[i], arr[0]
        heapsize -= 1
        max_heapify(arr, 0, heapsize)
    return arr

if __name__ == '__main__':
    print(heapsort([-996, 251, 0, 12, 8, 66]))

1. 堆数据结构

我们可以将堆视为一棵完全二叉树。但实际操作中并不需要定义二叉树类,只需要用数组表示即可。

(此段翻译及改写原文) 除了数组长度length以外,我们还可以定义一个属性heap-size,表示堆中多少元素存放在数组arr中。这就意味着不是所有数组中的元素都在堆中。用公式表达:0<= heap-size <= length

《算法导论》中以数组表示堆的图示如下:

image.png

在不使用二叉树结构,即只用数组表示堆的情况下,有如下性质:

#!/usr/bin/python3

from sys import maxsize as MAX

# 用数组表示堆
heap = [1, 2, 3, 4, 5]

# 以下方法只返回下标
# 书上的i从1开始。以下代码的i从0开始
def parent(i):
    if i:
        return (i // 2 - 1)
    return MAX

def left(i):
    return (2 * i + 1)

def right(i):
    return (2 * (i + 1))

2. 最大堆与最小堆

可以分为两种:

  • 最大堆
  • 最小堆

在最大堆中满足如下性质:

arr[parent(i)] >= a[i]

最小堆中刚好相反:

arr[parent(i)] <= arr[i]

3. 堆排序三步走

在本例中使用最大堆来进行堆排序。一般我们用最大堆进行堆排序,用最小堆实现优先队列。

可以将堆排序分解为三步:

  • max-heapify:时间复杂度为O(log n)
  • build-max-heap: 时间复杂度为O(n)
  • heapsort: 时间复杂度为O(n log n)

3.1 max-heapify方法

思想:max-heapify方法假设左右子树皆已满足二叉堆的性质,但树根可能比自己的孩子还小,所以将树根归位到应有的位置中。

教材上的伪代码改写为Python如下:

def max_heapify(arr, i, heapsize = 1):
    l = left(i)
    r = right(i)
    largest = len(arr) # 取一个怎么都取不到的值,用于初始化
    if l <= heapsize and arr[l] > arr[i]:
        largest = l
    else:
        largest = i
    if r <= heapsize and arr[r] > arr[largest]:
        largest = r
    if largest != i:
        arr[i], arr[largest] = arr[largest], arr[i]
        max_heapify(arr, largest, heapsize)
    return

3.2 build-max-heap方法

书上的伪代码翻译如下:

def build_max_heap(arr):
    heapsize = len(arr) - 1
    for i in range(len(arr) // 2, -1, -1):
        max_heapify(arr, i, heapsize)
    return heapsize

3.3 heapsort方法

翻译伪代码如下:

def heapsort(arr):
    heapsize = build_max_heap(arr)
    for i in range(len(arr) - 10, -1):
        arr[0], arr[i] = arr[i], arr[0]
        heapsize -= 1
        max_heapify(arr, 0, heapsize)
    return arr

References

  1. 《算法导论》