对堆的常见操作一般有两种,一种是新增一个值并将其置于堆顶,然后对堆进行调整,另一种是仅仅新增一个值,将元素放入堆的顶部,这样就带来两种不同的堆的建立。
第一种方式是通过每次将元素置于堆的底部,然后调整,采用类似插入排序的方法,保证每次调整后,都是最小堆。
时间复杂度O(NlogN)
#include <stdio.h>//建立一个堆第一种方法
int h[101];
void up(int i)//这个函数的目的是向一个已经符合最小堆的末尾插入元素,同时整理堆,使得它仍然符合最小堆的规律
{ //这里其实有一种递归的思想,假设第n层一下都符合最小堆的规律,那么如果第n-1层的某个节点也符合,那么就包含了第n-1层
int temp;
while (i!=1)
{
if (h[i]<h[i/2])//应该是父节点小,子节点大,如果父节点大,则不符合
{
temp=h[i];
h[i]=h[i/2];
h[i/2]=temp;
i=i/2;//不要忘记检查父节点
}
else break;
}
}
int main()
{
int n,i;
scanf("%d",&n);
for (i=1;i<=n;i++)
{
scanf("%d",&h[i]);//有点类似插入排序,读入一个数,就把他塞进堆内正确的位置,这样每个数读入之前都是符合最小堆的规律的,只需要将新加进来的数放在它正确的位置即可
up(i);
}
for (i=1;i<=n;i++)
printf("%d ",h[i]);
printf("\n");
return 0;
}
第二种方式是将所有数据读入,从最后一个非叶节点(n/2)开始,考察每棵树是否符合,之所以从非叶节点开始,是因为叶节点没有子节点,必然符合~~
时间复杂度O(N)
#include <stdio.h>
int a[101];
int n;
void down(int i)
{
int t,temp;
while(i*2<=n)
{
if (a[i]>a[i*2])
t=i*2;
else
t=i;
if ((i*2+1<=n)&&(a[t]>a[i*2+1]))
t=i*2+1;
if (t!=i)
{
temp=a[t];
a[t]=a[i];
a[i]=temp;
i=t;
}
else break;
}
}
int main()
{
int i;
scanf("%d",&n);
for (i=1;i<=n;i++)
scanf("%d",&a[i]);
for (i=n/2;i>=1;i--)
down(i);
for (i=1;i<=n;i++)
printf("%d ",a[i]);
return 0;
}
由此看来,一般第二种方式更加常见~~