二叉堆
是一种特殊的堆,二叉堆是完全二元树或者是近似完全二元树。
二叉堆有两种:
最大堆和最小堆。
最大堆:
父结点的键值总是大于或等于任何一个子节点的键值;
最小堆:
父结点的键值总是小于或等于任何一个子节点的键值。
堆一般用数组存放:跟节点为0节点,i节点的左子节点为2*i+1,右子节点为2*i+2;
堆一般用数组存放:跟节点为0节点,i节点的左子节点为2*i+1,右子节点为2*i+2;
堆的操作: 建立、插入、删除
插入:
因为从根节点到叶节点是一个有序序列。将新插入的节点放到最后然后将其放到这个有序序列。
删除:
删除某一个节点, 将最后一个节点填充到删除的节点处,然后将此节点看做成根节点构造一个堆。
堆的排序:
因为堆的根节点是最小堆值,所以每次取根节点,然后将其删除,剩余的节点重新构造成堆以此递归完成排序。
使用最小堆得到递增序列,最大堆得到递减序列。
由于每次重新恢复堆的时间复杂度为O(logN),共N - 1次重新恢复堆操作,再加上前面建立堆时N / 2次向下调整,每次调整时间复杂度也为O(logN)。二次操作时间相加还是O(N * logN)。故堆排序的时间复杂度为O(N * logN)。下面为代码以最小堆为例:
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
#define N 1000
int array[N];
int adjustHeap(int *a, int i, int n)
{
int j , temp;
temp = a[i];
j = 2*i + 1;
if(j < n)
{
if((j+1 < n) && (a[j+1] <= a[j]))
{
j++;
}
if(a[j] < temp)
{
a[i] = a[j];
a[j] = temp;
adjustHeap(a, j, n);
}
}
return 1;
}
//建立最小堆
int buildHeap(int *a, int n)
{
for(int i = n/2 - 1; i >= 0; i--)
adjustHeap(a, i, n);
return 1;
}
//删除节点i
int delHeap(int *a, int i, int n)
{
if(i < n)
{
swap(a[i], a[n-1]);
adjustHeap(a, i, n-1);
}
return 1;
}
//插入节点n
int insertHeap(int *a, int n)
{
if(n == 0)
return 1;
int j = n/2 -1;
if(a[n] >= a[j])
return 1;
swap(a[n], a[j]);
insertHeap(a, j);
return 1;
}
int sortHeap(int *a, int n)
{
int i = n;
while(i >=0 )
{
delHeap(a, 0, i);
i--;
}
return 1;
}
int main(int argc, char *argv[])
{
int a[N];
int num, i;
printf("请输入节点数量: ");
scanf("%d", &num);
printf("\n请输入%d个节点 以空格结束!\n", num);
for(i = 0; i < num; i++)
{
scanf("%d", &a[i]);
}
buildHeap(a, num);
printf("建成的堆:\n");
for(i = 0; i < num; i++)
{
printf("%d ", a[i]);
}
printf("\n");
delHeap(a, 6, num);
printf("删除后的堆:\n");
for(i = 0; i < num -1; i++)
{
printf("%d ", a[i]);
}
printf("\n");
a[num -1] = 14;
insertHeap(a, num-1);
printf("插入数据后的堆:\n");
for(i = 0; i < num; i++)
{
printf("%d ", a[i]);
}
printf("\n");
sortHeap(a, num);
printf("排序后的数据:\n");
for(i = 0; i < num; i++)
{
printf("%d ", a[i]);
}
printf("\n");
return 1;
}