这几天疯狂使用Binary Heap,所以打算写篇文章好好整理下这个数据结构。这个结构的 Insert
操作的时间复杂度是 O(n)
,但是他的 DeleteMin
操作能实现 O(1)
。因为完全二叉堆的结构很有规律,所以我们不需要用树结构来实现二叉堆,用数组即可。其中父节点在位置 i
上,左节点在位置 2i
上,右节点在 2i+1
上。
头文件
1 2 3 4 5 6 7
| struct HeapStruct { int Capacity; int Size; int *Elements; }; typedef struct HeapStruct *PriorityQueue;
|
初始化
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| PriorityQueue Init(int MaxElements) { PriorityQueue H; H = (PriorityQueue)malloc(sizeof(struct HeapStruct)); H->Elements = (int*)malloc((MaxElements+1)*sizeof(int)); H->Capacity = MaxElements; H->Size = 0; H->Elements[0] = MinData; return H; }
|
插入
1 2 3 4 5 6 7 8 9
| void Insert(int X, PriorityQueue H) { int i; for(i=++H->Size; H->Elements[i/2]>X; i/=2) H->Elements[i] = H->Elements[i/2]; H->Elements[i] = X; }
|
插入的策略是【上滤】。
我们先在堆的下一个可用位置新建一个空穴。比较空穴的父节点与待插入节点的大小。如果父节点比待插入元素小,说明该位置是待插入的位置。反之则将空穴上滤。
DeleteMin
其实可以看得出,堆的第一个元素就是最小值。所以我们要做的就是,返回第一个元素,然后调整堆结构。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| int DeleteMin(PriorityQueue H) { int i, Child; int MinElement, LastElement; MinElement = H->Elements[1]; LastElement = H->Elements[H->Size--]; for(i=1; i*2<=H->Size; i=Child) { Child = i * 2; if(Child != H->Size && \ H->Elements[Child+1] < H->Elements[Child]) Child++; if(LastElement > H->Elements[Child]) H->Elements[i] = H->Elements[Child]; else break; } H->Elements[i] = LastElement; return MinElement; }
|
这里使用的策略是【下滤】
我们在根节点处建立空穴,往下走,把子节点中较小的节点移到空穴处,直达达到叶节点。然后把堆的最后一个元素放到空穴中。