20. 二叉堆

一、什么是二叉堆

  二叉堆 也叫 优先队列(Priority Queue),它是一种特殊的队列,它取出元素的顺序是依照元素的 优先权(关键字)大小,而不是元素进入队列的先后顺序。二叉堆可以使用用数组表示的完全二叉树表示。任一结点的关键字是其子树所有结点的最大值或最小值。如果是最大值,则这个堆被称为 最大堆(MaxHeap)或者 大顶堆。如果是最大值,则这个堆被称为 最小堆(MinHeap)或者 小顶堆

二叉堆

ADT MaxHeap
{
Data:
    最大堆T∈MaxHeap, Item∈ElementType,
Operation:
    MaxHeap CreateMaxHeap(int MaxSize);             // 创建一个最大堆
    int IsFull(MaxHeap H);                          // 判断最大堆是否已满
    int IsEmpty(MaxHeap H);                         // 判断最大堆是否为空
    void Insert(MaxHeap H, ElementType X);          // 插入元素X
    ElementType Delete(MaxHeap H, ElementType X);   // 删除元素X
} ADT MaxHeap;

二、最大堆的表示

typedef int ElementType;

typedef struct HeapStruct
{
    ElementType * Data;
    int Size;
    int Capacity;
} HeapStruct, * MaxHeap;

三、创建堆

#include <stdlib.h>
#include <limits.h>

/**
 * @brief 最大堆的创建
 * 
 * @param MaxSize 最大容量
 * @return MaxHeap 指向最大堆的指针
 */
MaxHeap CreateMaxHeap(int MaxSize)
{
    MaxHeap H = (MaxHeap)malloc(sizeof(HeapStruct));
    H->Data = (ElementType *)malloc((MaxSize + 1) * sizeof(ElementType));
    H->Size = 0;
    H->Capacity = MaxSize;

    // 定义哨兵为大于堆中所有可能元素的值,便于后序操作
    H->Data[0] = INT_MAX;

    return H;
}

四、插入元素

/**
 * @brief 插入元素
 * 
 * @param H 最大堆
 * @param X 插入的元素
 */
void Insert(MaxHeap H, ElementType X)
{
    int i = 0;
  
    if (H->Size == H->Capacity)
    {
        printf("堆已满,无法插入元素\n");
        return;
    }

    // i指向插入后堆中的最后一个元素的位置
    i = ++H->Size;
    // 从最后一个元素开始,依次向上比较,直到找到合适位置
    for (; H->Data[i / 2] < X; i /= 2)
    {
        H->Data[i] = H->Data[i / 2];
    }
    // 插入元素
    H->Data[i] = X;
}

H->Data[0] 是哨兵元素,它不小于堆中大的最大元素,用于控制循环的结束。

五、删除元素

/**
 * @brief 删除元素
 * 
 * @param H 最大堆
 * @return ElementType 最大的元素
 */
ElementType Delete(MaxHeap H)
{
    int Parent = 0, Child = 0;
    ElementType MaxItem = 0, temp = 0;

    if (H->Capacity == 0)
    {
        printf("堆为空,无法删除元素\n");
        return 0;
    }

    MaxItem = H->Data[1];                                                       // 获取最大元素

    // 用最大堆中最后一个元素从根结点开始向下过滤下层结点
    temp = H->Data[H->Size--];
    // Parent指向当前结点,Parent * 2 指向左子结点
    for (Parent = 1; Parent * 2 <= H->Size ; Parent = Child)
    {
        Child = Parent * 2;
        // Child != H->Size,意味着有右子结点,Child指向左右子结点的较大者
        if (Child != H->Size && (H->Data[Child] < H->Data[Child + 1]))
        {
            Child++;
        }
        if (temp >= H->Data[Child])
        {
            break;                                                              // 找到合适位置,退出循环
        }
        else
        {
            H->Data[Parent] = H->Data[Child];                                   // 移动temp元素到下一层
        }
    }

    H->Data[Parent] = temp;
  
    return MaxItem;
}
posted @ 2023-07-23 19:58  星光樱梦  阅读(25)  评论(0编辑  收藏  举报