经常在学习一些相关的计算机知识时会遇到:xxx是基于堆的。乍一听上去感觉很高端,给人的第一印象就是一堆杂乱的数据摆在一起,然后进行特定的操作。实际上,堆(heap)是一棵特殊的完全二叉树,而且是采用的静态存储方式。它要求每个根节点都比他的左右子树结点值大(大顶堆)或者都小(小顶堆),若是我们每次取出根节点的值,然后删除掉根节点,对整个堆进行调整,再重复上面操作,这样就能得到一个有序的序列。

建堆的思想是,先逐个插入结点建立一棵完全二叉树(因为采用的静态存储,逐个赋值即可得到),然后对非叶结点逐个进行向下调整,调整完毕后即可得到堆,下面是调整和建堆操作:

void downAdjust(int low,int high)//将结点编号为low的结点向下调整,直到合适的位置
{
    int i=low,j=i*2;//i是待调整结点,j是其左子节点
    while(j<=high)//保证i不是叶子结点
    {
        if(j+1<=high&&heap[j+1]>heap[j])//若有右子节点,且其值大于左子节点,让j存储右子节点下标
            j=j+1;
        if(heap[j]>heap[i])//j表示子节点的最大值,如果比待调整结点大,交换其位置
        {
            swap(heap[i],heap[j]);
            i=j;//i向下移动,保持j为i的左子节点
            j=i*2;
        }
        else//若子节点比待比较结点小,则不用交换,直接退出
            break;
    }
}

void creat_heap()//完全二叉树有一半(上整)结点是叶子结点,是被动调整的,非叶结点范围是[1,n/2]。逐个调整即可建堆
{
    for(int i=n/2;i>=1;i--)//这里采用的是倒序枚举,每一次后面部分总是满足堆要求
    {
        downAdjust(i,n);
    }
}

对于堆,一般涉及的比较多的是堆排序算法,即上面提到的得到有序序列的方法。在每次删掉根节点后,调整的策略是把最后一个结点值赋予根节点,直接删除最后一个结点,然后将根节点向下调整,不断重复操作,即可得到堆排序如下:

void heapSort()
{
    creat_heap();//建堆
    for(int i=n;i>1;--i)//i始终对应最后一个结点编号
    {
        printf("%d\t",heap[1]);
        swap(heap[i],heap[1]);//交换根节点和最后一个结点的值
        downAdjust(1,i-1);//对根结点在剩余的n-1个结点中进行向下调整
    }
}

但到这里也只能算是个静态排序算法,因为没考虑插入操作,也就是说我们的堆一开始就是固定的,得到的排序结果也是固定的,下面讨论插入操作。对于二叉树而言,一般插入操作都是找到空指针插入然后做简单调整即可,由于对是一棵完全二叉树,在插入结点后还要保持这一特点,所以一般结点直接插入到最后,然后再对这个结点进行向上调整操作。

void upAdjust(int low,int high)//插入元素一般插在最底层最右边,需要向上调整
{
    int i=high;//i是要调整结点,j是其父结点
    int j=i/2;
    while(j>=low)
    {
        if(heap[j]<heap[i])//这里和下调不同,只需要一次比较
        {
            swap(heap[j],heap[i]);
            i=j;//i向上移动
            j=i/2;
        }
        else//已到最终位置
            break;
    }
}

void insertNode(int x)//插入结点值为x的结点
{
    heap[++n]=x;//将结点插入到最后
    upAdjust(1,n);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值