数据结构-最大堆

118 阅读3分钟

优先队列

概念

  • 普通队列:先进先出
  • 优先队列:出队顺序和入队顺序无关,和优先级相关

应用场景

  • 操作系统任务管理:动态选择优先级最高的任务执行
  • 在 N 个元素中选出前 M 个元素
    • 整体排序 NlogN
    • 使用优先队列 NlogM

image.png

  • 使用数组实现优先队列 时间复杂度: O(n^2)
  • 使用堆实现优先队列 时间复杂度: O(nlgn)

堆的基本存储

最大二叉堆

  • 堆中某个节点的值总是不大于其父节点的值
  • 堆保持是一棵完全二叉树
    • 除了最后一层外,其他层的节点必须是最大节点数
    • 最后一层要优先集中在左侧

image.png

  • parent(i) = i/2
  • left child(i) = 2*i
  • right child(i) = 2*i +1
  • 最后一个非叶子节点 = count/2

用数组实现最大二叉堆

max_heap.h

#ifndef MAX_HEAP_H
#define MAX_HEAP_H

// #include <algorithm>
#include <cassert>

using namespace std;

// 最大堆
template <typename Item>
class MaxHeap
{
private:
    Item *data;
    int count;
    int capacity;
    void shiftUp(int index);
    void shiftDown(int index);

public:
    MaxHeap(int capacity);
    MaxHeap(Item arr[], int arr_length);
    ~MaxHeap();
    int size();
    bool isEmpty();
    void insert(Item item);
    // 从最大堆中取出堆顶元素, 即堆中所存储的最大数据
    Item extractMax();
    // 获取最大堆中的堆顶元素
    Item getMax();
};

template <typename Item>
MaxHeap<Item>::MaxHeap(int capacity)
{
    // 索引0位置不存储数据
    data = new Item[capacity + 1];
    count = 0;
    this->capacity = capacity;
}

template <typename Item>
MaxHeap<Item>::MaxHeap(Item arr[], int arr_length)
{
    data = new Item[arr_length + 1];
    capacity = arr_length;

    for (int i = 0; i < arr_length; i++)
        data[i + 1] = arr[i];
    count = arr_length;

    for (int i = count / 2; i >= 1; i--)
        this->shiftDown(i);
}

template <typename Item>
MaxHeap<Item>::~MaxHeap()
{
    delete[] data;
}

template <typename Item>
int MaxHeap<Item>::size()
{
    return count;
}

template <typename Item>
bool MaxHeap<Item>::isEmpty()
{
    return count == 0;
}

template <typename Item>
void MaxHeap<Item>::insert(Item item)
{
    assert(count + 1 <= capacity);
    data[count + 1] = item;
    count++;
    this->shiftUp(count);
    // print();
}

template <typename Item>
Item MaxHeap<Item>::extractMax()
{
    assert(count > 0);
    Item heapTop = data[1];
    std::swap(data[1], data[count]);
    count--;
    this->shiftDown(1);
    // print();
    return heapTop;
}

template <typename Item>
Item MaxHeap<Item>::getMax()
{
    assert(count > 0);
    return data[1];
}

template <typename Item>
void MaxHeap<Item>::shiftUp(int index)
{
    // 索引1位置是根节点
    while (index > 1 && data[index / 2] < data[index])
    {
        std::swap(data[index / 2], data[index]);
        index = index / 2; // 父节点
    }
}

template <typename Item>
void MaxHeap<Item>::shiftDown(int index)
{
    // index*2 <= count 表示索引index节点有左孩子节点
    while (index * 2 <= count)
    {
        int temp = index * 2; // 初始化为左孩子节点
        if (index * 2 + 1 <= count)
        {
            // 当有右孩子节点时
            if (data[index * 2 + 1] > data[index * 2])
            {
                temp = index * 2 + 1;
            }
        }
        if (data[index] >= data[temp])
        {
            break;
        }
        std::swap(data[temp], data[index]);
        index = temp;
    }
}

#endif // MAX_HEAP_H

max_heap_test.cpp

#include <iostream>
#include "max_heap.h"

using namespace std;

// g++ max_heap_test.cpp && ./a.out
int main()
{
    MaxHeap<int> maxHeap(10);
    srand(time(NULL));
    for (int i = 0; i < 20; i++)
        maxHeap.insert(rand() % 100);

    while (!maxHeap.isEmpty())
    {
        cout << maxHeap.extractMax() << " ";
    }
    cout << endl;
    return 0;
}

从0开始

image.png

  • parent(i) = (i-1)/2
  • left child(i) = 2*i +1
  • right child(i) = 2*i +2
  • 最后一个非叶子节点 = (count-1)/2
#include <iostream>
#include "max_heap.h"

using namespace std;

template <typename T>
void __shiftDown(T arr[], int arr_length, int index)
{
    // index*2+1 < arr_length 表示索引index节点有左孩子节点
    while (index * 2 + 1 < arr_length)
    {
        int temp = index * 2 + 1; // 初始化为左孩子节点
        if (index * 2 + 2 < arr_length)
        {
            // 当有右孩子节点时
            if (arr[index * 2 + 2] > arr[index * 2 + 1])
            {
                temp = index * 2 + 2;
            }
        }
        if (arr[index] >= arr[temp])
        {
            break;
        }
        std::swap(arr[temp], arr[index]);
        index = temp;
    }
}

template <typename T>
void heap_sort(T arr[], int arr_length)
{
    // heapify
    for (int i = (arr_length - 1) / 2; i >= 0; i--)
        __shiftDown(arr, arr_length, i);
    for (int i = arr_length - 1; i > 0; i--)
    {
        swap(arr[0], arr[i]);
        __shiftDown(arr, i, 0);
    }
}

排序总结

image.png