前言:
理论(文字)知识很枯燥,但是如果读者真正想把一个问题搞明白,搞清楚,光靠成天撸代码是不行的,而需要理解其中的思想本质,否则只能是知其然而不知其所以然,我之前常说:“话不多说,直接上代码”希望不要误导读者。SO,请耐心的看下去,平常心,借用金庸老先生的一句话:“他强由他强,清风拂山岗;他横由他横,明月照大江.他自狠来他自恶,我自一口真气足。”
最后,希望各位同行者可以提出问题,我们共同进步。
涉及到的知识点:
1. 堆只是局部有序:其父节点一定大于儿子节点、但是兄弟节点之间并没有必然的大小关系。
2. 从逻辑上来讲、堆是一种树形结构,是用数组来存储的完全二叉树。
3.建立堆的方法(从下标为0的位置开始存放):首先将所有关键码放到一维数组中,此时形成的完全二叉树并不具备堆的特性,但是仅包含叶子节点的子树已经是堆,即在右n个节点的完全二叉树中,当i > n/2 - 1时,以关键码Ki为根的子树已经是堆。这时,从含有内部节点数最少的子树(这种子树在完全二叉树的倒数第二层,此时i = n/2 - 1开始,从右至左依次进行调整。对这一层调整完成后,继续对上一层进行同样的工作,直到整个过程到达树根,整棵完全二叉树就成为一个堆。
4. 堆的插入:首先将新添的元素加入末尾。然后为了保持堆的的性质,需要沿着其祖先的路径,自下而上依次比较和交换该节点与父节点的位置,直到重新满足堆的性质。插入过程总是自下而上的,最后停留在满足堆性质的位置。
5. 堆的删除:删除的操作处理和插入时方向相反。首先把最末端的节点填入这个位置,然后从这个新填入的位置开始自上而上的为满足堆的性质而依次比较和交换该节点与父节点的位置,直到满足堆的性质 。
完整代码如下:(下标从1开始存放关键码的最大堆)
接下来。。。
当然是上代码了
/* 堆(heap) - 优先队列 */ /* 最大堆 */ #include <iostream> #include <malloc.h> const int MaxSize = 5; const int MAXDATA = 1000; //最大堆的哨兵值 const int ERROR = -1; //错误标识应根据具体情况定义为堆中不可能出现的元素值 using namespace std; typedef int ElementType; //堆的类型定义 typedef struct hnode { ElementType *data; //存储元素的数组 int size; //堆中当前元素的个数 int capacity; //堆的最大容量 } hnode, *heap; typedef heap MaxHeap; //最大堆 typedef heap MinHeap; //最小堆 //创建容量为MaxSize的空的最大堆 MaxHeap CreateHeap( int MaxSize ) { MaxHeap h = new hnode; h->data = new int[MaxSize+1]; //因为下标从一开始存放 h->size = 0; h->capacity = MaxSize; h->data[0] = MAXDATA; //定义"哨兵"为大于堆中所有可能元素的值 return h; } //判满 bool IsFull( MaxHeap h ) { return( h->size == h->capacity ); } //将元素插入最大堆(h)中,其中h->data[0]已经定义为哨兵 //堆的插入 - 首先将要插入元素添加到堆数组末尾 //然后自下而上依次比较和交换该节点和父节点的位置,直到重新满足堆的性质为止 bool Insert( MaxHeap h, ElementType x ) { int i; if( IsFull(h) ) { cout << "最大堆已满" << endl; return false; } // 插入 - 注意堆的规模要增大 i = ++h->size ; // i - 指向插入后堆中的最后一个元素的位置 for( ; h->data[i/2] < x; i /= 2 ) //x(儿子节点) > 他的父节点 { h->data[i] = h->data[i/2]; //上滤 - x ,将父节点的值与儿子节点互换位置 } h->data[i] = x; //将x插入 return true; } //判空 bool IsEmpty( MaxHeap h ) { return( h->size == 0 ); } //从最大堆(h)中取出键值最大(优先级最高)的元素,并删除一个节点 ElementType DeleteMax( MaxHeap h ) { int parent, child; ElementType MaxItem, x; if( IsEmpty(h) ) { cout << "最大堆已为空" << endl; return ERROR; } MaxItem = h->data[1]; //取出根节点存放的最大值 //用最大堆中的最后一个元素从根节点开始向上过滤下层节点 x = h->data[h->size--]; // 删除 - 注意堆的规模要减小 //自上而下比较和交换父节点和左右儿子中较大的那个节点 //parent指的是我从堆末尾拿上来的元素值该放的位置,循环结束时的parent就是我们要找的位置 for( parent = 1; parent*2 <= h->size; parent = child ) //parent*2 - 父节点parent左儿子的位置 { child = parent*2; if( (child != h->size) && (h->data[child] < h->data[child+1])) child++; //child指向左右节点中的较大者 if( x >= h->data[child] ) break; //找到了合适的位置,跳出 else //下滤x h->data[parent] = h->data[child]; } h->data[parent] = x; return MaxItem; } //建造最大堆 MaxHeap PercDown( MaxHeap h, int p ) { //下滤:将 h 中以 h->data[p] 为根的子堆调整为最大堆 int parent, child; ElementType x; x = h->data[p]; //取出根节点存放的值 for( parent = p; parent*2 <= h->size; parent = child ) { child = parent*2; if( (child != h->size) && (h->data[child] < h->data[child+1]) ) child++; // child指向左右子节点的较大者 if( x > h->data[child] ) break; //找到了合适位置 else //下滤x h->data[parent] = h->data[child]; } h->data[parent] = x; return h; } MaxHeap BuildHeap( MaxHeap h ) { //这里调整 h->data[] 中的元素,使满足最大堆的有序性 //这里假设所有 h->size 个元素已经存到 h->data[]中 int i; //从最后一个节点的父节点开始,到根节点1 for( i = h->size/2; i > 0; i-- ) { h = PercDown( h, i ); } return h; } int main() { int i; MaxHeap h; h = CreateHeap(MaxSize); for( i = 1; i <= MaxSize; ++i ) { cin >> h->data[i]; h->size++; } h = BuildHeap(h); for( i = 1; i <= MaxSize; ++i ) { cout << h->data[i] << " "; } return 0; }
运行结果:
上述输入输出对应的完全二叉树:
初始:5 1 8 2 9
建好的最大堆: