堆是一种优先队列,堆有两个特性:
- 结构性:用数组表示的完全二叉树;
- 有序性:任一结点的关键字是其子树所有结点的最值。如果是最大值,则成为最大堆;如果是最小值,称为最小堆。
基于第一个特性,可以用一个一维数组去存这个堆,起始下标为1。
对于堆的操作,下面只给出最大堆的插入和删除的操作。
插入元素
如图所示:
其主要思想是:
- 先将元素放入数组的最后一个位置
- 将这个元素进行向上调整
代码如下:
//heap[]:堆数组
//maxsize:当前堆中元素个数
void InsertItem(int x)
{
int i;
/*i初始为最后一个位置+1,循环执行的条件为父亲结点比儿子结点的元素要大并且还没有到根节点
(或者在heap[0]处设置一个哨兵:最大堆中设置一个最大值即可)*/
for(i = ++maxsize; heap[i/2]>heap[i] && i>1; i /= 2)
heap[i] = heap[i/2];//将比x大的往下过滤
heap[i] = x;
}
时间复杂度T(N)=O(logN)
删除元素
如图所示:
其主要思想是:
- 删除根节点
- 把最后一个结点移到根结点处
- 从根节点开始向下调整
代码如下:
int deletemax()
{
int maxitem = heap[1];//取出根节点最大值
int x = heap[maxsize--];//先把最后一个结点放入x中
int child, parent;/*parent表示最后一个结点应该放入的位置,
child表示parent的儿子结点*/
for(parent = 1; parent*2 <= maxsize; parent = child)
{/*从根节点开始往下过滤:
parent=1:先假设要调整到根结点处
parent*2<=maxsize:判断是否有左儿子
parent=child:向下调整*/
child = parent*2;
if(child!=maxsize && heap[child]<heap[child+1])
child++;//选取左右儿子中较大的那个
if(x >= heap[child]) break;/*如果当前调整到的位置已经
比左右儿子大了*/
else heap[parent] = heap[child];//否则,向下调整
}
heap[parent] = x;
return maxitem;
}
时间复杂度T(N)=O(logN)
最大堆、最小堆的建立(线性时间复杂度)
主要思想:
- 先将n个元素顺序存入heap[ ]数组中
- 从最后一个结点的父亲结点开始调整,使其所在的子树成为堆。那么在被调整结点依次往上走的时候,它的左子树、右子树就都是堆了,这个时候调整起来就会方便很多。
代码如下:
/* 以建立最大堆为例 */
#include <stdio.h>
void siftdown(int *heap, int p, int maxsize)
{
int x = heap[p];
int child, parent;
for(parent = p; parent*2 <= maxsize; parent = child)
{
child = parent*2;
if(child!=maxsize && heap[child]<heap[child+1])
child++;
if(x >= heap[child]) break;
else heap[parent] = heap[child];
}
heap[parent] = x;
}
void buildmaxheap(int *heap, int maxsize)
{
int i;
for(i = maxsize/2; i >= 1; i--)//从最后一个结点的父亲节点开始调整
siftdown(heap, i, maxsize);
}
int main()
{
int heap[100], n, i;
scanf("%d", &n);
for(i = 1; i <= n; i++) scanf("%d", &heap[i]);//先顺序存入数组中
buildmaxheap(heap, n);
return 0;
}
最小堆代码类似,就不给出了。。。
时间复杂度为O(n) (堆中各个结点的高度之和)
堆排序(以从小到大排为例)
主要思想:
- 首先把元素顺序存入数组中
- 将其调整为最大堆
- 将根节点(最大值)与最后一个结点互换(因为最大值肯定是放在最后的),然后除掉最后一个元素(实际上还在堆中),将剩下元素调整为最大堆。
- 重复步骤三,直到排序成功。
(ps:如果是从大到小排序,则调整为最小堆)
如图所示:
代码如下:
#include <stdio.h>
void swap(int *a, int *b)
{
int temp = *a;
*a = *b;
*b = temp;
}
void siftdown(int *heap, int p, int capacity)
{
int x = heap[p];
int child, parent;
for(parent = p; parent*2 <= capacity; parent = child)
{
child = parent*2;
if(child!=capacity && heap[child]<heap[child+1])
child++;
if(x >= heap[child]) break;
else heap[parent] = heap[child];
}
heap[parent] = x;
}
void heapsort(int *heap, int maxsize)
{
int i;
//先调整为最大堆
for(i = maxsize/2; i >= 1; i--)
siftdown( heap, i, maxsize);
for(i = maxsize; i > 1; i--)/*i:每次把最后一个元素与根节点元素交换,初始化为maxsize
{ i>1:因为只有两个元素的时候,肯定有序,所以直接退出循环
i--:相当于每次把最大值排除在外了,i--变为了最后一个结点*/
swap(&heap[1], &heap[i]);//把第一个和当前最后一个结点交换
siftdown(heap, 1, i-1);//i-1:把排除了最后一个元素的剩余元素调整为最大堆
}
}
int main()
{
int heap[100], n, i;
scanf("%d", &n);
for(i = 1; i <= n; i++) scanf("%d", &heap[i]);//先顺序存入数组中
heapsort(heap, n);
for(i = 1; i <= n; i++) printf("%d%c", heap[i], i == n ? '\n' : ' ');
return 0;
}
时间复杂度T(N)=O(NlogN)