堆【C++实现】

本文详细介绍了堆的基本概念,包括堆的定义、性质以及堆排序方法。随后探讨了堆的实现,提供了C++代码示例,重点讲解了如何使用STL中的priority_queue进行操作,包括建堆、插入、弹出等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

目录

1.基本概念

(1)堆的定义

(2)堆的性质

(3)堆排序

2.堆的实现

(1)数据结构

(2)建堆

(3)插入一个结点

(4)弹出堆顶

(5)返回堆顶

(6)其它操作

3.STL——priority_queue

(1)建堆

(2)操作


1.基本概念
(1)堆的定义

n个元素的序列{arr[1],arr[2],...,arr[n]},当且仅当满足下列关系时,称之为堆:  

  • arr[i]<=arr[2*i] && arr[i]<=arr[2*i+1] || arr[i]>=arr[2*i] && arr[i]>=arr[2*i+1], i∈[1,n/2]

相应的:  

  • 当arr[i]<=arr[2*i] && arr[i]<=arr[2*i+1], i∈[1,n/2] 时,称之为小顶堆
  • 当arr[i]>=arr[2*i] && arr[i]>=arr[2*i+1], i∈[1,n/2] 时,称之为大顶堆
(2)堆的性质

由定义可知,堆其实就是一棵完全二叉树(顺序表示),故堆满足完全二叉树的所有性质。另外,小顶堆的堆顶元素是序列中的最小值,大顶堆的堆顶元素是序列中的最大值。

(3)堆排序

若在输出堆顶元素之后,使得剩余n-1个元素的序列又重建称一个堆,则可以得到序列中的次小(大)值。如此反复执行,便能得到一个有序序列,这个过程称为堆排序。

2.堆的实现
(1)数据结构
#include <iostream>
#include <vector>
#include <algorithm>                 // swap

class Heap{
private:
    std::vector<int> heap;
public:
    Heap(){};                        // 初始化空堆
    Heap(std::vector<int> &arr);     // 由一个无序序列建成堆
    void push(int val);              // 插入一个结点
    void pop();                      // 弹出堆顶
    int top();                       // 返回堆顶
    int size();                      // 返回结点个数
    bool empty();                    // 判空
};
(2)建堆

算法一:直接将无序序列建成堆  

将无序序列拷贝到heap,然后从最后一个分支结点直至堆顶(heap[n/2],heap[n/2-1],...,heap[1]),对每个分支结点,得到它与所有孩子中的最小(大)值,然后将该分支结点与最小(大)值对应的结点交换;  

T(n)=O(n)  

S(n)=O(n)  


算法二:初始化空堆,然后依次插入无序序列中的元素  

T(n)=O(nlogn)(每插入一个元素需要O(logn),下面会讲)  

S(n)=O(n)  

Heap::Heap(std::vector<int> &arr):heap(arr){
    for(int parent=heap.size()/2,child=parent*2; parent>0; parent--,child=2*parent){
        if(child+1<heap.size() && heap[child+1]<heap[child]){// 小顶堆,找最小孩子
        // if(child+1==heap.size() && heap[child+1]>heap[child]){// 大顶堆,找最大孩子
            child++;
        }
        if(heap[child] < heap[parent]){// 小顶堆,与最小孩子交换
        // if(heap[child] < heap[parent]){// 大顶堆,与最大孩子交换
            std::swap(heap[child], heap[parent]);
        }
    }
}
(3)插入一个结点

先将新结点插入heap末尾,然后向上调整堆:  

将heap末尾元素视为当前结点,判断它是否有父结点,且当前结点是否小于(大于)父结点。若都满足则交换二者,然后视其父结点为当前结点继续向上判断,直至堆顶或父节点大于(小于)当前结点。  

T(n)=O(logn)  

S(n)=O(1)  

void Heap::push(int val){
    heap.push_back(val);
    for(int child=heap.size()-1,parent=child/2; parent>0; child=parent,parent/=2){
        if(heap[child] < heap[parent]){// 小顶堆,小于父结点要交换
        // if(heap[child] > heap[parent]){// 大顶堆,大于父结点要交换
            std::swap(heap[child], heap[parent]);
        }else{
            break;
        }
    }
}
(4)弹出堆顶

将堆顶与序列末尾元素交换,删除序列末尾元素,然后向下调整堆:  

将堆顶视为当前结点,判断它是否有孩子结点,若有则将它与最小(最大)孩子交换,然后视被交换的孩子结点为当前结点继续向下进行该操作,直至叶子结点。  

T(n)=O(logn)  

S(n)=O(1)  

void Heap::pop(){
    swap(heap.front(),heap.back());
    heap.pop_back();

    for(int parent=1,child=2; child<heap.size(); parent=child,child*=2){
        if(child+1<heap.size() && heap[child+1]<heap[child]){// 小顶堆,找最小孩子
        // if(child+1<heap.size() && heap[child+1]>heap[child]){// 大顶堆,找最大孩子
            child++;
        }
        if(heap[child] < heap[parent]){// 小顶堆,与最小孩子交换
        // if(heap[child] > heap[parent]){// 大顶堆,与最大孩子交换
            std::swap(heap[child], heap[parent]);
        }else{
            break;
        }
    }
}
(5)返回堆顶
int Heap::top(){
    return heap.front();
}
(6)其它操作
int Heap::size(){
    return heap.size();
}

bool Heap::empty(){
    return heap.empty();
}
3.STL——priority_queue
(1)建堆
// 1.小根堆(默认优先级,std::less<T>)
    priority_queue<T> heap();                // T(n)=O(1)
    priority_queue<T> heap(iter1, iter2);    // T(n)=O(n)
// 2.大根堆(std::greater<T>)
    priority_queue<T, std::vector<T>, std::greater<T>> heap;                // T(n)=O(1)
    priority_queue<T, std::vector<T>, std::greater<T>> heap(iter1, iter2);  // T(n)=O(n)
// 3.自定义优先级(若a的优先级大于(小于)b,则为小(大)根堆)
    priority_queue<T, std::vector<T>, decltype([](auto& a,auto& b){
        return bool_expression;
    })> heap;                                                        // T(n)=O(1)
    priority_queue<T, std::vector<T>, decltype([](auto& a,auto& b){
        return bool_expression;
    })> heap(iter1, iter2);                                          // T(n)=O(n)
(2)操作
// 1.插入一个结点                    T(n)=O(logn)
    void push(const T& node);
    void push(T&& node);
    void emplace(Args&&... args);
// 2.弹出堆顶                        T(n)=O(logn)
    void pop();
// 3.返回堆顶                        T(n)=O(1)
    const T& top();
// 4.判空                            T(n)=O(1)
    bool empty() const;
// 5.返回结点数                      T(n)=O(1)
    size_t size() const;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值