二叉堆是完全二叉树,实现 查询最值,插入元素,删除任意元素(堆顶或其他),修改任意元素值 的功能,复杂度为O(logN)
由于是完全二叉树,所以用数组实现,方便快捷
法一:之前常用的普通实现,没进行封装,也可以写一个封装实现的
//Binary Heap,the most common implement of Poriority Queen(Heap)
//以最小堆为例
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct Heap{
int capacity;
int size;
int *data;
};
struct Heap* Init(struct Heap*binaryHeap,int n)//初始化
{
binaryHeap=(struct Heap*)malloc(sizeof(struct Heap));
binaryHeap->capacity=n;
binaryHeap->size=0;
binaryHeap->data=(int*)malloc(sizeof(int)*(n+1));
binaryHeap->data[0]=-10000;
return binaryHeap;
}
void Destory(struct Heap* binaryHeap)//释放
{
free(binaryHeap->data);
free(binaryHeap);
return;
}
int IsEmpty(struct Heap *binaryHeap)//判断堆空不空
{
return binaryHeap->size==0;
}
int IsFull(struct Heap* binaryHeap)//判断堆满不满
{
return binaryHeap->size==binaryHeap->capacity;
}
void Insert(struct Heap* binaryHeap,int number)//插入
{
int i;
if(!IsFull(binaryHeap)){
binaryHeap->size++;
//percolated up 上滤
for(i=binaryHeap->size;i>1&&binaryHeap->data[i/2]>number;i/=2)
binaryHeap->data[i]=binaryHeap->data[i/2];
binaryHeap->data[i]=number;
}
return;
}
void DeleteMin(struct Heap* binaryHeap)//删除最小
{
int i,child;
int minElement;
int lastElement;
if(!IsEmpty(binaryHeap)){
//minElement=binaryHeap->data[1];
if(binaryHeap->size==1){
binaryHeap->size--;
return;
}
lastElement=binaryHeap->data[binaryHeap->size--];
//percolated down 下滤
for(i=1;i*2<=binaryHeap->size;i=child){//此处用i*2判断很重要
child=i*2;
if(child!=binaryHeap->size&&binaryHeap->data[child+1]<binaryHeap->data[child])
child++;
if(lastElement>binaryHeap->data[child])
binaryHeap->data[i]=binaryHeap->data[child];
else
break;
}
binaryHeap->data[i]=lastElement;
return;
}
return;
}
void BuildHeap(struct Heap* binaryHeap)//建堆
{
int n,temp,child,j;
scanf("%d",&n);
if(n>binaryHeap->capacity)
return;
for(int i=1;i<=n;i++)
scanf("%d",&(binaryHeap->data[++binaryHeap->size]));
for(int i=binaryHeap->size/2;i>=1;i--){//逐一下滤
temp=binaryHeap->data[i];
for(j=i;j*2<=binaryHeap->size;j=child){//此处用j*2判断很重要
child=2*j;
if(child!=binaryHeap->size&&binaryHeap->data[child+1]<binaryHeap->data[child])
child=child+1;
if(temp>binaryHeap->data[child])
binaryHeap->data[j]=binaryHeap->data[child];
else
break;
}
binaryHeap->data[j]=temp;
}
return;
}
void Delete(struct Heap* binaryHeap,int loc)//删除某个节点的值
{
int i,number;
if(loc<=0||loc>binaryHeap->size)
return;
//先把此处的值变为近似无限小
binaryHeap->data[loc]=-100000000;
number=binaryHeap->data[loc];
//上滤
for(i=loc;i>1&&number<binaryHeap->data[i/2];i--)
binaryHeap->data[i]=binaryHeap->data[i/2];
binaryHeap->data[i]=number;
//删除目前位于1处的节点
DeleteMin(binaryHeap);
return;
}
void IncreaseKey(struct Heap* binaryHeap,int loc,int increasement)//增加某个节点的键值
{
int i,number,child;
if(loc<=0||loc>binaryHeap->size||increasement<=0)
return;
binaryHeap->data[loc]+=increasement;
//下滤
number=binaryHeap->data[loc];
for(i=1;i*2<=binaryHeap->size;i=child){//此处用i*2判断很重要
child=i*2;
if(child!=binaryHeap->size&&binaryHeap->data[child+1]<binaryHeap->data[child])
child++;
if(number>binaryHeap->data[child])
binaryHeap->data[i]=binaryHeap->data[child];
else
break;
}
binaryHeap->data[i]=number;
return;
}
void DecreaseKey(struct Heap* binaryHeap,int loc,int decreasement)//减少某个节点的键值 假设decreasement为正
{
int i,number,child;
if(loc<=0||loc>binaryHeap->size||decreasement<=0)
return;
binaryHeap->data[loc]-=decreasement;
//上滤
number=binaryHeap->data[loc];
for(i=loc;i>=1&&binaryHeap->data[i/2]>number;i/=2)
binaryHeap->data[i]=binaryHeap->data[i/2];
binaryHeap->data[i]=number;
return;
}
void levelOrderTrversal(struct Heap* binaryHeap)//按照节点编号打印节点数值
{
for(int i=1;i<=binaryHeap->size;i++)
printf("%d:%d%c",i,binaryHeap->data[i],i==binaryHeap->size?'\n':' ');
return;
}
int main(){
struct Heap *binaryHeap;
binaryHeap=Init(binaryHeap,100);
BuildHeap(binaryHeap);
levelOrderTrversal(binaryHeap);
Insert(binaryHeap,10);
levelOrderTrversal(binaryHeap);
DeleteMin(binaryHeap);
levelOrderTrversal(binaryHeap);
Delete(binaryHeap,2);
levelOrderTrversal(binaryHeap);
IncreaseKey(binaryHeap,1,7);
levelOrderTrversal(binaryHeap);
DecreaseKey(binaryHeap,3,7);
levelOrderTrversal(binaryHeap);
return 0;
}
法二:基于C++类封装实现,但思路不同上,多出两个数组,id[MAXN],用于记录 队中位置为 i 的元素是第几个插入堆中的,pos[MAXN],用于记录 第 i 个插入堆中的元素在堆中的位置。此思路没写不基于封装的。
以最小堆为例
#include "stdafx.h"
#include <cstdio>
#include <cstring>
#include <iostream>
#include <fstream>
#include <sstream>
using namespace std;
#define MAXN 100005
#define MINNUM -0X3f3f3f3f
int arr[MAXN];
class BinaryHeap {
private:
int n, counter;
int heap[MAXN], id[MAXN], pos[MAXN];
public:
BinaryHeap():n(0),counter(0){}
BinaryHeap(int arr[], int offest):n(0),counter(0) {
for (int i = 0; i < offest; i++) {
heap[++n] = arr[i];
id[n] = n;
pos[n] = n;
}
for (int i = n / 2; i >= 1; i--) {
down(i);
}
}
void push(int te) {
heap[++n] = te;
id[n] = ++counter;
pos[counter] = n;
up(n);
}
int pop() {
swap(heap[1], heap[n]);
swap(id[1], id[n]);
pos[id[1]] = 1;
pos[id[n--]] = -1;
down(1);
return heap[n + 1];
}
void change(int i,int value) {//i指的是第几个插入的
if (pos[i] == -1) {
cerr << "ERROR" << endl;
return;
}
heap[pos[i]] = value;
down(pos[i]);
up(pos[i]);
//也可写成:
//int t = heap[i];heap[i] = value;
//if(value > t) down(i);else if(value < t) up(i);
}
void erase(int i) {//i指的是第几个插入的
if (pos[i] == -1) {
cerr << "ERROR" << endl;
return;
}
heap[pos[i]] = MINNUM;
up(pos[i]);
pop();
}
void up(int i) {//i指的是heap下标
int j;
int x = heap[i], y = id[i];
for (j = i; j > 1 && heap[j] < heap[j / 2]; j /= 2) {
heap[j] = heap[j / 2];
id[j] = id[j/2];
pos[id[j]] = j;
}
heap[j] = x;
id[j] = y;
pos[y] = j;
}
void down(int i) {//i指的heap是下标
int j,child;
int x = heap[i], y = id[i];
for (j = i; j*2 <= n; j = child) {
child = j * 2;
if (child + 1 < n && heap[child + 1] < heap[child]) {
child++;
}
if (x > heap[child]) {
heap[j] = heap[child];
id[j] = id[child];
pos[id[j]] = j;
}
else {
break;
}
}
heap[j] = x;
id[j] = y;
pos[y] = j;
}
};
int main() {
int n;
BinaryHeap *binaryHeap;
while (scanf("%d", &n) != EOF) {
for (int i = 0; i < n; i++) {
cin >> arr[i];
}
binaryHeap = new BinaryHeap(arr, n);
cout << binaryHeap->pop() << endl;
//...
//...
//...
//...
delete binaryHeap;
}
return 0;
}