STL heap相关算法

STL heap相关算法

内容

C++ 标准库提供了一组底层堆操作算法(定义在 <algorithm> 头文件中),这些函数允许直接操作序列式容器(如 vectorarray)来构造和操作堆。这些算法与 priority_queue 的区别在于它们 更灵活、更底层,允许开发者直接控制堆结构。以下是核心函数及其用法详解:


核心堆算法列表

函数名 作用 时间复杂度
std::make_heap 将无序序列转换为堆结构 O(n)
std::push_heap 向堆的末尾添加元素,并调整堆结构 O(log n)
std::pop_heap 移除堆顶元素,将其放到末尾并调整堆 O(log n)
std::sort_heap 将堆结构转换为有序序列(会破坏堆性质) O(n log n)
std::is_heap 检查序列是否为堆 O(n)
std::is_heap_until 返回最长满足堆性质的子序列的末尾迭代器 O(n)

1. std::make_heap:构建堆

  • 功能:将指定范围内的元素调整为堆结构。
  • 参数(first, last[, comp])comp 为可选的比较器)。
  • 示例
    #include <algorithm>
    #include <vector>
    
    std::vector<int> v = {3, 1, 4, 1, 5, 9};
    std::make_heap(v.begin(), v.end()); // 默认构建最大堆
    // v 变为 {9, 5, 4, 1, 1, 3}(堆结构,不一定是完全有序)
    
    • 若需要最小堆,传递 std::greater<> 比较器:
      std::make_heap(v.begin(), v.end(), std::greater<int>());
      

2. std::push_heap:向堆中添加元素

  • 功能:假设容器末尾是新插入的元素,调整堆结构使其仍满足堆性质。
  • 前提:容器在调用前已满足堆性质(除了最后一个元素)。
  • 示例
    v.push_back(6); // 先在容器末尾添加元素
    std::push_heap(v.begin(), v.end()); // 调整堆
    // v 变为 {9, 5, 6, 1, 1, 3, 4}(最大堆)
    

3. std::pop_heap:移除堆顶元素

  • 功能:将堆顶元素(最大值或最小值)移动到容器末尾,并调整剩余元素为堆。
  • 示例
    std::pop_heap(v.begin(), v.end()); // 将堆顶移动到末尾
    // v 变为 {5, 4, 3, 1, 1, 9}(末尾元素 9 是原堆顶)
    int max_value = v.back(); // 获取被移除的堆顶元素
    v.pop_back(); // 实际删除末尾元素
    

4. std::sort_heap:堆排序

  • 功能:将堆结构转换为有序序列(升序或降序)。
  • 注意:调用后原堆结构被破坏。
  • 示例
    std::sort_heap(v.begin(), v.end()); // 升序排序
    // v 变为 {1, 1, 3, 4, 5, 9}
    

5. std::is_heapstd::is_heap_until

  • 用途:验证堆结构或调试堆操作。
    std::vector<int> v = {9, 5, 4, 1, 1, 3};
    bool is_heap = std::is_heap(v.begin(), v.end()); // 检查是否为堆
    auto it = std::is_heap_until(v.begin(), v.end()); // 返回第一个破坏堆性质的元素位置
    

堆算法 vs priority_queue

特性 堆算法 priority_queue
底层容器访问 可直接操作容器(如 vector 封装容器,只能通过 top() 访问堆顶
灵活性 可动态调整堆大小或部分处理堆元素 只能通过 push()/pop() 操作
内存控制 可复用现有容器内存 需要容器适配器(默认 vector
适用场景 需要精细控制堆结构(如实现特定算法) 简单的优先级队列需求

使用场景示例

场景 1:动态维护 Top-K 元素

std::vector<int> data = {/* 流式数据 */};
std::vector<int> top_k(10); // 保留前 10 大元素

// 1. 构建最小堆(保留最大值)
std::make_heap(top_k.begin(), top_k.end(), std::greater<int>());

for (int num : data) {
    if (num > top_k.front()) { // 当前元素比堆顶大
        // 替换堆顶并调整堆
        top_k[0] = num;
        std::make_heap(top_k.begin(), top_k.end(), std::greater<int>());
    }
}

场景 2:手动实现优先队列

std::vector<int> heap;

// 插入元素
heap.push_back(new_element);
std::push_heap(heap.begin(), heap.end());

// 取出堆顶
std::pop_heap(heap.begin(), heap.end());
heap.pop_back();

性能注意事项

  • make_heap 的代价:频繁调用 make_heap(如场景 1)的时间复杂度为 O(k),若数据流较大,应改用 push_heap/pop_heap(时间复杂度 O(log k))。
  • 缓存友好性:堆操作在内存访问上不如数组紧凑,对性能敏感的场景可考虑使用更紧凑的数据结构(如 d-ary heap)。

总结

C++ 的堆算法提供了对堆结构的底层控制,适合需要 动态调整堆大小、复用内存或实现特定堆逻辑 的场景。而 priority_queue 作为容器适配器,更适合简单的优先级队列需求。理解这些算法可以让你在需要优化性能或实现复杂逻辑时,更灵活地操作堆结构。

posted @ 2025-03-28 19:18  Gold_stein  阅读(54)  评论(0)    收藏  举报