一、堆
堆就是一个数组,不过这个数组中数据的存储可以用完全二叉树来展现,树上的每一个节点对应堆中的一个元素。父节点和左右子节点的对应关系为:left(i) = (i >> 1) + 1, right(i) = (i >> 1) + 2, parent(i) = (i - 1) >> 1.
堆分为最大堆和最小堆。最大堆特点是除了根节点之外,所有节点都满足 A[parent(i)] >= A[i],即:节点的值不大于父节点。这样,根节点存放的值就是最大的。最小堆特点是除了根节点之外,所有节点都满足 A[parent(i)] <= A[i],这样,在堆中,根节点存放的是最小值。
堆的操作包括上浮、下沉、建堆。下面以最大堆为例说明这些操作:
下沉:如果某节点的左右子树都是最大堆,该节点的值又小于其中一个节点的值,则交换该节点和子节点,使根节点下沉,然后递归处理交换后的节点,直到根节点下沉到正确的位置。
C代码为:
堆分为最大堆和最小堆。最大堆特点是除了根节点之外,所有节点都满足 A[parent(i)] >= A[i],即:节点的值不大于父节点。这样,根节点存放的值就是最大的。最小堆特点是除了根节点之外,所有节点都满足 A[parent(i)] <= A[i],这样,在堆中,根节点存放的是最小值。
堆的操作包括上浮、下沉、建堆。下面以最大堆为例说明这些操作:
下沉:如果某节点的左右子树都是最大堆,该节点的值又小于其中一个节点的值,则交换该节点和子节点,使根节点下沉,然后递归处理交换后的节点,直到根节点下沉到正确的位置。
C代码为:
/*
arr为数组,index为要处理的元素下标,HeapSize为数组大小
*/
void sinkHeap(int* arr, int index, int HeapSize)
{
if (index < HeapSize)
{
int left = (index << 1) + 1;
int right = (index << 1) + 2;
int largest = index;
if (left < HeapSize && arr[left] > arr[index])
{
largest = left;
}
if (right < HeapSize && arr[right] > arr[largest])
{
largest = right;
}
if (largest != index)
{
int tmp = arr[index];
arr[index] = arr[largest];
arr[largest] = tmp;
MaxHeap(arr, largest, HeapSize);
}
}
}
上浮:如果某节点父节点以上都是最大堆,该节点的值又大于父节点,则交换该节点和父节点,使此节点上浮,然后递归该节点,直到上浮到正确的位置。
C代码为:
/*
arr为数组,index为要处理的元素下标
*/
void riseHeap(int* arr, int index)
{
if (index > 0)
{
int parent = (index - 1) >> 1;
if (arr[index] > arr[parent])
{
int tmp = arr[index];
arr[index] = arr[parent];
arr[parent] = tmp;
rise(arr, parent);
}
}
}
建堆:
如果用下沉的方法,建堆处理为:从最后一个非叶子节点开始循环直到根节点,依次调用sinkHeap
c代码为:
void buildHeap(int* arr, int HeapSize)
{
for(int i = (HeapSize >> 1) - 1; i >= 0; --i)
{
MaxHeap(arr, i, HeapSize);
}
}
如果用上浮方法,建堆处理为:从第二个元素开始直到最后一个元素,依次调用riseHeap
C代码为:
void build(int* arr, int HeapSize)
{
for (int i = 1; i < HeapSize; ++i)
{
rise(arr, i);
}
}
二、堆排序
堆排序是一个时间复杂度为O(nlgn)的非稳定算法。堆排序的思想为:先建堆,然后循环把堆顶元素和最后元素交换,此时堆可能被破坏,需要对堆顶元素做下沉操作,保持堆的结构。
C代码为:
void HeapSort(int* arr, int len)
{
build(arr, len);
for (int i = len - 1; i > 0; --i)
{
int tmp = arr[0];
arr[0] = arr[i];
arr[i] = tmp;
sinkHeap(arr, 0, i);
}
}
三、优先队列
优先队列是用来维护一组元素构成的集合S的数据结构,其中每个元素都有一个相关的值,称为关键字key。优先队列支持的操作有:
insert(S,value,len):把值为value的元素插入集合S中。
maximum(S):返回S中具有最大键值的元素
extractMax(S, len):删除并返回集合S中具有最大键值的元素
increaseKey(S,index,value):将下标为index的元素的值增加到value
具体实现方法及代码为:
insert(S,value,len):把值为value的元素插入集合S中。
maximum(S):返回S中具有最大键值的元素
extractMax(S, len):删除并返回集合S中具有最大键值的元素
increaseKey(S,index,value):将下标为index的元素的值增加到value
具体实现方法及代码为:
获取最大元素:
void maximum(const int* arr)
{
return arr[0];
}
删除时候,需要重建堆:
int extractMax(const int* arr, int & HeapSize)
{
--HeapSize;
int max = arr[0];
arr[0] = arr[HeapSize];
sinkHeap(arr, 0, HeapSize);
return max;
}
增加值,增大某元素的值,然后对该元素上浮:
void increaseKey(int* arr, int index, int value)
{
arr[index] = value;
rise(arr, index);
}
增添元素,在底部增加叶子节点,然后对该元素上浮:
void insert(int* arr, int value, int & HeapSize)
{
HeapSize++;
arr[HeapSize-1] = value;
rise(arr, HeapSize-1);
}