01 开门见山
堆排序的时间复杂度是O(n*lgn). 因为堆排序是树形结构. "堆"的数据结构来进行信息管理.不仅用在堆排序中, 而且也可以构造一种有效的优先队列.
02 堆
(二叉)堆是一个数组, 可以看成近似完全的二叉树. 树上的每个结点对应数组中的一个元素. 该树是从左向右填充. 如图:
左边是二叉树的形式,右边是数组的形式, 此为大顶堆.
最大堆
就是父结点大于等于子结点的堆.最大元素是在树的根节点.
最小堆
就是父结点小于等于子结点的堆.最小元素是在树的根节点.
建立最大堆过程 如图:
图a包括10个元素的输入数组及其对应的二叉树. 图a显示的是下面代码中 Build_Heap_Max中for循环开始时的位置,图b是下一次迭代的位置, 图c-e是后序的迭代操作,图f是最后最大堆的结果
03 建最大堆代码
# coding=utf-8
import math
def Parent(i):
"""
返回i的父节点索引,向下取整
"""
return int(math.floor((i) / 2))
def Left(i):
"""
返回一个节点的左节点索引
"""
return 2 * i
def Right(i):
"""
返回一个节点的有节点的索引
"""
return 2 * i + 1
def Max_Heapify(A, i, heap_size):
"""
建立一个堆
"""
if isinstance(A, list):
l = Left(i)
r = Right(i)
if heap_size >= l and A[l - 1] > A[i - 1]:
largest = l
else:
largest = i
if heap_size >= r and A[r - 1] > A[largest - 1]:
largest = r
if largest != i:
A[i - 1], A[largest - 1] = A[largest - 1], A[i - 1]
Max_Heapify(A, largest, heap_size)
else:
return "A 必须为列表"
def Build_Heap_Max(A):
"""
建立最大堆
"""
if isinstance(A, list):
heap_size = len(A)
for i in range(int(math.floor(heap_size / 2)), 0, -1):
Max_Heapify(A, i, heap_size)
else:
return "A 必须为列表"
04 堆排序算法
了解上面的基础后, 就可以开始主题了. 看代码:
def Heap_Sort(A):
"""
堆排序
"""
if isinstance(A, list):
Build_Heap_Max(A)
heap_size = len(A)
for i in range(len(A), 1, -1):
A[1 - 1], A[i - 1] = A[i - 1], A[1 - 1]
heap_size = heap_size - 1
Max_Heapify(A, 1, heap_size)
return A
else:
return "A 必须为列表"
if __name__ == '__main__':
A = [4, 1, 3, 2, 16, 9, 10, 14, 8, 7]
print("排序前: ", A)
print("排序后: ", Heap_Sort(A))
a图是执行Build_Heap_Max(A)构造得到的最大堆, b-j是每次执行Max_Heapify(A, 1, heap_size)后得到的最大堆, 并标出当前的循环的值.k为最终的结果.
05 优先队列
优先队列是堆的一个应用分为:最大优先队列和最小优先队列. 优先队列是一种用来维护由一组元素构成的集合S的数据结构,其中的每一个元素都有一个相关的值, 称为关键字. 最大优先队列支持操作有:
1、INSERT(S, x):将x插入优先队列.
2、MAXIMUM(S): 返回S中具有最大值的元素.
3、EXTRACT-MAX(S):去掉并返回S中最大值的元素.
4、INCREASE-KEY(S, x, k):将x的值增加到k,假设k的值不小于x的值
最大优先队列的应用有很多, 其中一个就是在共享计算机系统的作业调度. 最大优先队列记录将要执行的各个作业,和他们之间的优先级.一个作业完成或中断后, 调度器调用EXTRACT-MAX从所有的等待作业中, 选出最高优先级的作业来执行.调度器可以用INSERT把一个新作业加入到队列中. 这个应用有点类 似rabbitmq, kafka等消息队列.下面看最大优先队列代码:
def Heap_Maximum(A):
"""
返回A中最大值
"""
return A[0]
def Heap_Extract_Max(A):
"""
返回最大值,并从A中移除
"""
heap_size = len(A)
if heap_size < 1:
print("error heap underflow")
return
Max_A = A[0]
A[0] = A[heap_size - 1]
heap_size = heap_size - 1
Max_Heapify(A, 1, heap_size)
return Max_A
def Heap_Increase_Key(A, i, key):
"""
增加优先队列中key的值
"""
if key < A[i - 1]:
print("error new key is smaller than current key")
return
A[i - 1] = key
while i > 1 and A[Parent(i - 1)] < A[i - 1]:
A[i - 1], A[Parent(i - 1)] = A[Parent(i - 1)] = A[i - 1]
i = Parent(i)
def Max_Heap_Insert(A, key):
"""
扩展优先队列
"""
heap_size = len(A)
heap_size += 1
A[heap_size - 1] = float("-inf")
Heap_Increase_Key(A, heap_size, key)
06 C语言版最大堆
#include <stdio.h>
#include <stdlib.h>
void swap(int* a, int* b) {
int temp = *b;
*b = *a;
*a = temp;
}
void max_heapify(int arr[], int start, int end) {
int dad = start;
int son = dad * 2 + 1;
while (son <= end) {
if (son + 1 <= end && arr[son] < arr[son + 1])
son++;
if (arr[dad] > arr[son])
return;
else {
swap(&arr[dad], &arr[son]);
dad = son;
son = dad * 2 + 1;
}
}
}
void heap_sort(int arr[], int len) {
int i;
for (i = len / 2 - 1; i >= 0; i--)
max_heapify(arr, i, len - 1);
for (i = len - 1; i > 0; i--) {
swap(&arr[0], &arr[i]);
max_heapify(arr, 0, i - 1);
}
}
int main() {
int arr[] = { 3, 5, 3, 0, 8, 6, 1, 5, 8, 6, 2, 4, 9, 4, 7, 0, 1, 8, 9, 7, 3, 1, 2, 5, 9, 7, 4, 0, 2, 6 };
int len = (int)(sizeof(arr) / sizeof(*arr));
heap_sort(arr, len);
int i;
for (i = 0; i < len; i++)
printf("%d ", arr[i]);
printf("\n");
return 0;
}
总之, 在一个包含n个元素的堆中, 所有优先队列的操作都可以在O(lgn)时间内完成.