堆(heap)/C++

1.解释:

在这里“堆”是一种特殊的树形数据结构,它满足堆的特性:父节点的值一定不大于或不小于子节点的值。堆被认为在计算机算法中起到重要作用,并被用于各种编程语言,例如c++、Java等中。        百度百科

(英语:Heap)是计算机科学中的一种特别的树状数据结构。若是满足以下特性,即可称为堆:“给定堆中任意节点P和C,若P是C的母节点,那么P的值会小于等于(或大于等于)C的值”。若母节点的值恒小于等于子节点的值,此堆称为最小堆(min heap);反之,若母节点的值恒大于等于子节点的值,此堆称为最大堆(max heap)。在堆中最顶端的那一个节点,称作根节点(root node),根节点本身没有母节点(parent node)。                维基百科

引申(可跳过):在Java中堆是Java虚拟机JVM的内存数据区。Heap 的管理很复杂,每次分配不定长的内存空间,专门用来保存对象的实例。在Heap 中分配一定的内存来保存对象实例,实际上也只是保存对象实例的属性值,属性的类型和对象本身的类型标记等,并不保存对象的方法(方法是指令,保存在Stack中),在Heap 中分配一定的内存保存对象实例和对象的序列化比较类似。而对象实例在Heap 中分配好以后,需要在Stack中保存一个4字节的Heap内存地址,用来定位该对象实例在Heap 中的位置,便于找到该对象实例。由于Stack的内存管理是顺序分配的,而且定长,不存在内存回收问题;而Heap 则是随机分配内存,不定长度,存在内存分配和回收的问题;

2.实现:

a.相关函数:

通过vector和完全二叉树实现。

make_heap(start,end,cmp)建立堆
push_heap(start,end) 在vector中加好了(push_back),再使用这个函数重新调整一下堆
pop_heap() 使用后,root 结点就被放到了vector的最后,再使用vector的pop_back()删除
sort_heap(start,end,cmp)堆排序

示例:

#include<vector>
#include<iostream>
#include<algorithm>

using namespace std;

bool cmp(const int& a, const int& b)
{
    return a > b;
}


int main()
{
    int a[10] = {1,5,2,6,3,7,8,4,9,0};
    vector<int> v1(a,a+10);
    //make_heap(v1.begin(),v1.end(),greater<int>());//降序 
    //sort_heap(v1.begin(),v1.end(),greater<int>());

    //make_heap(v1.begin(),v1.end(),less<int>());//升序 
    //sort_heap(v1.begin(),v1.end(),less<int>());

    make_heap(v1.begin(),v1.end(),cmp);//自定义cmp,降序 
    sort_heap(v1.begin(),v1.end(),cmp);

    for(int i=0; i<10; i++){
        cout << v1[i]<<";";
    }

    cout<<endl;

    return 0;
}

b.编程实现(详细代码包括两个文件Heap.h以及HeapTest.cpp:)

Heap.h

//STL堆算法实现(大顶堆)  
  
//包含容器vector的头文件:Heap用vector来存储元素  
#include <vector>  
#include <iostream>  
#include <functional>  
  
#define MAX_VALUE 999999 //某个很大的值,存放在vector的第一个位置(最大堆)  
  
const int StartIndex = 1;//容器中堆元素起始索引  
  
using namespace std;  
  
//堆类定义  
//默认比较规则less  
template <class ElemType,class Compare = less<ElemType> >  
class MyHeap{  
private:  
    vector<ElemType> heapDataVec;//存放元素的容器  
    int numCounts;//堆中元素个数  
    Compare comp;//比较规则  
  
public:  
    MyHeap();  
  
    vector<ElemType> getVec();  
  
    void initHeap(ElemType *data,const int n);//初始化操作  
    void printfHeap();//输出堆元素  
    void makeHeap();//建堆  
    void sortHeap();//堆排序算法  
    void pushHeap(ElemType elem);//向堆中插入元素  
    void popHeap();//从堆中取出堆顶的元素  
    void adjustHeap(int childTree,ElemType adjustValue);//调整子树  
    void percolateUp(int holeIndex,ElemType adjustValue);//上溯操作  
};  
  
template <class ElemType,class Compare>  
MyHeap<ElemType,Compare>::MyHeap()  
:numCounts(0)  
{  
    heapDataVec.push_back(MAX_VALUE);  
}  
  
template <class ElemType,class Compare>  
vector<ElemType> MyHeap<ElemType,Compare>::getVec()  
{  
    return heapDataVec;  
}  
  
template <class ElemType,class Compare>  
void MyHeap<ElemType,Compare>::initHeap(ElemType *data,const int n)  
{  
    //拷贝元素数据到vector中  
    for (int i = 0;i < n;++i)  
    {  
        heapDataVec.push_back(*(data + i));  
        ++numCounts;  
    }  
}  
  
template <class ElemType,class Compare>  
void MyHeap<ElemType,Compare>::printfHeap()  
{  
    cout << "Heap : ";  
    for (int i = 1;i <= numCounts;++i)  
    {  
        cout << heapDataVec[i] << " ";  
    }  
    cout << endl;  
}  
  
template <class ElemType,class Compare>  
void MyHeap<ElemType,Compare>::makeHeap()  
{  
    //建堆的过程就是一个不断调整堆的过程,循环调用函数adjustHeap依次调整子树  
    if (numCounts < 2)  
        return;  
    //第一个需要调整的子树的根节点多音  
    int parent = numCounts / 2;  
    while(1)  
    {  
        adjustHeap(parent,heapDataVec[parent]);  
        if (StartIndex == parent)//到达根节点  
            return;  
  
        --parent;  
    }  
}  
  
template <class ElemType,class Compare>  
void MyHeap<ElemType,Compare>::sortHeap()  
{  
    //堆排序思路  
    //每执行一次popHeap操作,堆顶的元素被放置在尾端,然后针对前面的一次再执行popHeap操作  
    //依次下去,最后即得到排序结果  
    while(numCounts > 0)  
        popHeap();  
}  
  
template <class ElemType,class Compare>  
void MyHeap<ElemType,Compare>::pushHeap(ElemType elem)  
{  
    //将新元素添加到vector中  
    heapDataVec.push_back(elem);  
    ++numCounts;  
  
    //执行一次上溯操作,调整堆,以使其满足最大堆的性质  
    percolateUp(numCounts,heapDataVec[numCounts]);  
}  
  
template <class ElemType,class Compare>  
void MyHeap<ElemType,Compare>::popHeap()  
{  
    //将堆顶的元素放在容器的最尾部,然后将尾部的原元素作为调整值,重新生成堆  
    ElemType adjustValue = heapDataVec[numCounts];  
    //堆顶元素为容器的首元素  
    heapDataVec[numCounts] = heapDataVec[StartIndex];  
    //堆中元素数目减一  
    --numCounts;  
  
    adjustHeap(StartIndex,adjustValue);  
}  
  
//调整以childTree为根的子树为堆  
template <class ElemType,class Compare>  
void MyHeap<ElemType,Compare>::adjustHeap(int childTree,ElemType adjustValue)  
{  
    //洞节点索引  
    int holeIndex = childTree;  
    int secondChid = 2 * holeIndex + 1;//洞节点的右子节点(注意:起始索引从1开始)  
    while(secondChid <= numCounts)  
    {  
        if (comp(heapDataVec[secondChid],heapDataVec[secondChid - 1]))  
        {  
            --secondChid;//表示两个子节点中值较大的那个  
        }  
  
        //上溯  
        heapDataVec[holeIndex] = heapDataVec[secondChid];//令较大值为洞值  
        holeIndex = secondChid;//洞节点索引下移  
        secondChid = 2 * secondChid + 1;//重新计算洞节点右子节点  
    }  
    //如果洞节点只有左子节点  
    if (secondChid == numCounts + 1)  
    {  
        //令左子节点值为洞值  
        heapDataVec[holeIndex] = heapDataVec[secondChid - 1];  
        holeIndex = secondChid - 1;  
    }  
    //将调整值赋予洞节点  
    heapDataVec[holeIndex] = adjustValue;  
  
    //此时可能尚未满足堆的特性,需要再执行一次上溯操作  
    percolateUp(holeIndex,adjustValue);  
}  
  
//上溯操作  
template <class ElemType,class Compare>  
void MyHeap<ElemType,Compare>::percolateUp(int holeIndex,ElemType adjustValue)  
{  
    //将新节点与其父节点进行比较,如果键值比其父节点大,就父子交换位置。  
    //如此,知道不需要对换或直到根节点为止  
    int parentIndex = holeIndex / 2;  
    while(holeIndex > StartIndex && comp(heapDataVec[parentIndex],adjustValue))  
    {  
        heapDataVec[holeIndex] = heapDataVec[parentIndex];  
        holeIndex = parentIndex;  
        parentIndex /= 2;  
    }  
    heapDataVec[holeIndex] = adjustValue;//将新值放置在正确的位置  
}  

HeapTest.cpp

#include "Heap.h"  
  
#include <iostream>  
  
using namespace  std;  
  
int main()  
{  
    const int n = 9;  
    int data[n] = {0,1,2,3,4,8,9,3,5};  
  
    MyHeap<int> *intHeapObj = new MyHeap<int>;  
  
    intHeapObj->initHeap(data,n);  
    intHeapObj->printfHeap();  
  
    intHeapObj->makeHeap();  
    intHeapObj->printfHeap();  
  
    intHeapObj->pushHeap(7);  
    intHeapObj->printfHeap();  
  
    intHeapObj->popHeap();  
    cout << "The top of heap :" << intHeapObj->getVec().back() << endl;  
    intHeapObj->getVec().pop_back();  
    intHeapObj->printfHeap();  
  
    intHeapObj->sortHeap();  
    cout << "Sorted data :";  
    for (int i = 1;i <= n;++i)  
        cout << intHeapObj->getVec()[i] << " ";  
    cout << endl;  
        delete intHeapObj;  
    return 0;  
}  

3.总结

堆是一种完全二叉树,因此我们可以用数组来存储所有节点。

堆并不是STL的组件,但是经常充当着底层实现结构。比如优先级队列(Priority Queue)https://blog.csdn.net/a22222259/article/details/98626474

在这里的实现中,采用了一个技巧:将数组中索引为0的元素保留,设置为极大值或者为极小值(依据大顶堆或者小顶堆而定)。那么当某个节点的索引是i时,其左子节点索引为2*i,右子节点索引为2*i+1.父节点是i/2(这里/表示高斯符号,取整)。这种以数组表示树的方式,我们成为隐式表述法(implicit reprentation)。我们这里用C++ STL中的容器vector实现替代数组的功能。

 

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值