堆
二叉堆(以大根堆为例):
堆的数据结构:
堆一般使用数组存储。当堆中有n个元素的时,可以将这些元素存放在数组heap的前n个单元里,其中堆的根节点中元素存放在heap[1]中。结点之间的关系有两种情况:
- 如果根节点在数组中的位置是1,那么第i个位置的左右节点下标分别为2i、2i+1,父节点下标为 i/2。
- 如果根节点在数组中的位置是0,那么第i个位置的左右节点下标分别为2i+1、2i+2,父节点下标为 (i-1)/2。
struct Heap
{
public:
//EType为自定义类型
int *heap; //存放数据的空间,下标从1开始存储数据,下标为0的作为工作空间,存储临时数据。
int HeapSize; //HeapSize是堆元素的个数
int MaxSize; //MaxSize是堆中最多存放元素数量
Heap(int maxsize) //init
{
MaxSize = maxsize;
HeapSize = 0;
heap = new int[maxsize + 1];
}
~Heap() //free memory
{
delete heap;
}
};
初始化堆:
int Maxsize = 20;
Heap *H = new Heap(Maxsize);
交换堆中元素:
//把堆中的a,b位置的值互换
void swap(Heap &H, int a, int b)
{
//临时存储child位置的值
int temp = H.heap[a];
//把index的值赋给child的位置
H.heap[a] = H.heap[b];
//把原来的child位置的数值赋值给index位置
H.heap[b] = temp;
}
向上调整:
递归实现:
void HeapUp(Heap &H, int index)
{
//注意由于数值是从下标为1开始,当index = 1的时候,已经是根节点了
if (index > 1)
{
//求出父亲的节点
int parent = index / 2;
//获取相应位置的数值
int parentValue = H.heap[parent];
int indexValue = H.heap[index];
//如果父亲节点比index的数值小,就交换二者的数值
if (parentValue < indexValue)
{
//交换数值
swap(H, parent, index);
//递归调用
HeapUp(H, parent);
}
}
}
非递归实现:
void HeapUp(Heap &H, int index)
{
int parent = 0;
while (index > 1)
{
//获取index的父节点的下标
parent = index / 2;
//获得父节点的值
int parentValue = H.heap[parent];
//获得index位置的值
int indexValue = H.heap[index];
//如果父节点的值小就交换
if (parentValue < indexValue)
{
swap(H, parent, index);
}
index = parent;
}
}
向下调整:
递归实现:
void HeapDown(Heap &H, int index)
{
int n = H.HeapSize;
//记录最大的那个儿子节点的位置
int child = -1;
if (2 * index > n)
{
//2*index>n说明该节点没有左右儿子节点了,那么就返回
return;
}
else if (2 * index == n)
{
//如果只有一个儿子(左儿子节点)
child = 2 * index;
}
else if (2 * index < n)
{
//如果左右儿子都存在
//定义左儿子节点
child = 2 * index;
//如果左儿子小于右儿子的数值,取右儿子的下标
if (H.heap[child] < H.heap[child+1])
{
child++;
}
}
if (H.heap[child] > H.heap[index])
{
//交换堆中的child,和index位置的值
swap(H, child, index);
//完成交换后递归调用,继续下降
HeapDown(H, child);
}
}
非递归实现:
void HeapDown(Heap &H, int index)
{
int child = 0;//存储左儿子的位置
int temp = H.heap[index];
int n = H.HeapSize;
//如果有儿子的话
while (2 * index <= n)
{
//获取左儿子的位置
child = 2 * index;
//如果有右儿子且右儿子数值比左儿子大
if (child < n&&H.heap[child + 1]>H.heap[child])
{
child = child + 1;
}
//如果数值最大的儿子比temp的值大
if (H.heap[child] > temp)
{
swap(H, index, child);
index = child;
}
else
break;
}
}
插入元素:
void Insert(Heap &H, int value)
{
//如果堆没满,堆数据数量加一,最后一个位置放入新数据
if (H.HeapSize < H.MaxSize)
{
H.HeapSize++;
H.heap[H.HeapSize] = value;
//最后一个数据上升到合适位置
HeapUp(H, H.HeapSize);
}
else
return;
}
删除元素:
oid Delete(Heap &H, int index)
{
//把最后的一个叶子的数值赋值给index位置
H.heap[index] = H.heap[H.HeapSize];
//堆元素个数减一
H.HeapSize--;
//index节点下沉到合适位置
HeapDown(H, index);
}
调整堆:
void Adjust(Heap &H)
{
//从第Heapsize/2个元素到第1个元素逐个HeapDown
int n = H.HeapSize / 2;
while (n >= 1)
{
HeapDown(H, n);
--n;
}
}
堆排序:
void HeapSort(Heap &H, int a[], int length)
{
for (int i = 0; i < length; ++i)
Insert(H, a[i]);
while (H.HeapSize > 0)
{
cout << H.heap[1] << endl;
Delete(H, 1);
}
}
d叉堆:
d叉堆与二叉堆的区别是d叉堆是完全d叉树。
| 操作 | 二叉堆 | d叉堆 |
|---|---|---|
| insert | O(logN) | O(log(d)N) |
| delete | O(logN) | O(d*log(d)N) |
| merge | O(N*logN) | O(N*log(d)N) |

浙公网安备 33010602011771号