ITPub博客

首页 > IT职业 > IT职场 > 堆(heap)的原理与实现详解【Van0512】

堆(heap)的原理与实现详解【Van0512】

IT职场 作者:蚂蚁没问题 时间:2021-09-09 08:57:01 0 删除 编辑

本文主要包括的内容:

  • 堆的原理
  • 建堆、调堆
  • C++中 priority_queue (Java中也有一个 PriorityQueue,有必要再补上)
  • 手动建堆的实例(C++类实现)

先吐个槽O(∩_∩)O:
网站上的编辑器我总是不太会用,排版总是让我很揪心,大概是写得太少了吧。慢慢习惯吧,希望每一篇文章的排版都能进步一点点。言归正传。


堆(heap)
是一种优先队列(priority queue)。
取出元素的顺序是按照元素的优先权(关键字)大小,而不是元素进入队列的先后顺序。
特性:

  1. 结构性:用数组表示的完全二叉树。
  2. 有序性:任一节点的关键字是其子树所有节点的最大(小)值。
    MaxHeap、MinHeap

主要操作:建堆、判空、判满、插入、删除堆顶元素。

直接生成堆的时间复杂性是O(nlog(n))。
在线性时间复杂度O(n)下建堆:

  1. 将N个元素按输入顺序存入,先满足完全二叉树(complete binary tree)的结构特性。
  2. 调整节点位置:从最后一个节点的父节点开始。

这边提供一份C语言的代码,主要关注它的原理:


/*----------- 建造最大堆 -----------*/
void PercDown( MaxHeap H, int p )
{ /* 下滤:将H中以H->Data[p]为根的子堆调整为最大堆 */
    int Parent, Child;
    ElementType X;

    X = H->Data[p]; /* 取出根结点存放的值 */
    for( Parent=p; Parent*2Size; Parent=Child ) {
        Child = Parent * 2;
        if( (Child!=H->Size) && (H->Data[Child]Data[Child+1]) )
            Child++;  /* Child指向左右子结点的较大者 */
        if( X >= H->Data[Child] ) break; /* 找到了合适位置 */
        else  /* 下滤X */
            H->Data[Parent] = H->Data[Child];
    }
    H->Data[Parent] = X;
}

void BuildHeap( MaxHeap H )
{ /* 调整H->Data[]中的元素,使满足最大堆的有序性  */
  /* 这里假设所有H->Size个元素已经存在H->Data[]中 */

    int i;

    /* 从最后一个结点的父节点开始,到根结点1 */
    for( i = H->Size/2; i>0; i-- )
        PercDown( H, i );
}

重要算法:上虑、下虑。


在C++中,提供了一种容器适配器(container adaptor)来实现这种数据结构,放在头文件 queue 中。
具体使用方式:


#include 
using namespace std;
...
priority_queue priQue;

主要操作有:


    bool empty()        //Test whether container is empty.
    int size()          //Return size.
    void push(elem)     //Insert element.
    void emplace(elem)  //Construct and insert element.
    void pop()          //Remove top element.
    elemType top()      //Access top element.

priority_queue 默认按照严格弱序(strict weak ordering)排序规则生成最大堆,若要生成最小堆,看下面这个例子:


    #include 
    #include 
    #include 
    #include 
    using namespace std;

    int main(void) {
        priority_queue priQue;
        for (int i = 0; i  b;
                }
            };
            在下面的一行代码中,把 greater 改成 greater 即可,头文件 functional 也可删除。
        */
        priority_queue, greater> minHeap;
        for (int i = 0; i 

但许多时候,常常有其他的需求。为此,我们就需要手动建一个堆,然后就可以在堆上增加一些函数,提供我们想要的功能。
写一个最小堆类:


typedef int ElemType;
class MinHeap {
public:
    MinHeap(int capacity);  //建堆,传入堆的容量。
    ~MinHeap();
    bool isEmpty() const;
    bool isFull() const;
    bool insertElem(ElemType &elem);
    ElemType deleteTop();
private:
    ElemType *arr;
    int capacity;
    int len;    //堆中元素个数,是堆的最后一个元素的索引。
};

/*
数组长度为堆容量+1。
0号元素作为“哨兵”,在最小堆中,设置为比所有元素都小的值。
1号元素作为根节点,那么父子节点下标有如下关系:
    leftChild = parent * 2;
    rightChild = parent * 2 + 1;
    parent = Child/2;
*/
MinHeap::MinHeap(int capacity) {
    arr = new ElemType[capacity + 1];
    this->capacity = capacity;
    len = 0;
    arr[0] = INT_MIN;    //定义在头文件 climits 里面
}

MinHeap::~MinHeap() {
    delete[] arr;
    arr = NULL;
}

bool MinHeap::isEmpty() const {
    return 0 == len;
}

bool MinHeap::isFull() const {
    return len == capacity;
}

bool MinHeap::insertElem(ElemType &elem) {
    if (isFull()) return false;
    /*
    上滤。
    如果父节点的数据值比elem大,那么把父节点的数据值拷贝到要插入的子节点中。
    把子节点的下标更新为父节点,继续。
    若父节点的数据值比elem小,那么elem就可以插入。
    */
    int child = ++len;
    for (; arr[child / 2] > elem; child /= 2)
        arr[child] = arr[child / 2];        //上滤 child = parent
    arr[child] = elem;
    return true;
}

ElemType MinHeap::deleteTop() {
    if (isEmpty()) return INT_MIN;  //error
    ElemType minItem = arr[1];
    /*
    下滤。
    模拟将最后一个元素从根节点的位置向下层节点过滤的过程。
    */
    ElemType tmp = arr[len--];
    int parent = 1;
    for (int child = 2 * parent; child  arr[child + 1]) child++;    //child指向数据值较小的子节点。
        if (tmp 

比如,如果要打印从一个给定索引到根节点的路径,就可以在 MinHeap 中增加一个函数,代码如下:

class MinHeap {
public:
    void printPath(int index);
}

void MinHeap::printPath(int index) {
    if (index > len) {
        cout  1) {
        index /= 2;
        cout 

好了,就写到这里吧。
慕友若有什么高见,可在下面回复。
祝开心。

来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/2568/viewspace-2807382/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2019-08-16

  • 博文量
    107
  • 访问量
    675