排序算法-堆排序与优先队列

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)时间内完成.

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

算法小筑

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

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

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

打赏作者

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

抵扣说明:

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

余额充值