优先队列
概念
- 普通队列:先进先出
- 优先队列:出队顺序和入队顺序无关,和优先级相关
应用场景
- 操作系统任务管理:动态选择优先级最高的任务执行
- 在 N 个元素中选出前 M 个元素
- 整体排序 NlogN
- 使用优先队列 NlogM
- 使用数组实现优先队列 时间复杂度: O(n^2)
- 使用堆实现优先队列 时间复杂度: O(nlgn)
堆的基本存储
最大二叉堆
- 堆中某个节点的值总是不大于其父节点的值
- 堆保持是一棵
完全二叉树- 除了最后一层外,其他层的节点必须是最大节点数
- 最后一层要优先集中在左侧
- 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开始
- 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);
}
}