堆操作

堆操作

堆是完全二叉树,因此用数组表示比较方便,i为当前结点,则 2*i(若存在) 为i的左孩子,2*i+1(若存在) 为i的右孩子。

堆中每个结点的值都不小于左右孩子的值(大顶堆),或都不大于左右孩子的值(小顶堆),所以每个结点都是以其为根节点的子树的最大(小)值。

int heap[maxn], n;      // 定义堆数组以及元素个数,注意完全二叉树数组第一个元素的下标必须为1

为了使每个结点都放在正确的地方,以大顶堆为例,需要将其与左右子结点进行比较,并与其中较大的进行交换,到达新位置后,要与新的左右子结点进行比较,重复以上步骤,直到该结点比它所有子结点都大或不存在子结点。这个过程是从当前结点开始,不断向下调整的,因此有以下代码:

/* 向下调整 */
void downAdjust(int i, int end)     // i为当前结点,end为堆最后一个结点
{
    int j = 2 * i;      // 左子结点
    while (j <= end)
    {
        if (j + 1 <= end && heap[j + 1] > heap[j])      // 先比较左右子结点
            j = j + 1;
            
        if (heap[i] < heap[j])      // 根结点小于子结点,则向下移动
        {
            swap(heap[i], heap[j]);
            i = j;
            j = 2 * i;
        }
        else        // 已经比所有子结点大或没有子结点
            break;
    }
}

在向下调整的基础上建堆:

/* 建堆 */
void createHeap()
{
    for (int i = n / 2; i >= 1; i--)    // 从下往上从右往左,针对所有非叶结点向下调整
        downAdjust(i, n);
}

如果需要删除堆顶结点,只要用堆中最后一个结点覆盖堆顶结点,并使堆结点数-1,再对堆顶结点进行向下调整即可:

/* 删除堆顶元素 */
void deleteTop()
{
    heap[1] = heap[n--];        // 覆盖并减少数量
    downAdjust(1, n);       // 将新的堆顶结点移动到正确的地方
}

向堆中插入结点,只要在堆末尾插入结点,并不断将插入结点与其父结点比较,若大于父结点则向上移动,直到小于等于父结点为止:

/* 向上调整 */
void upAdjust(i, begin)     // begin为堆中第一个结点,i为插入结点
{
    int j = i / 2;      // j为父结点
    while (j >= begin)
    {
        if (heap[i] > heap[j])      // 插入结点大于父结点
        {
            swap(heap[i], heap[j]);
            i = j;
            j = i / 2;
        }
        else        // 插入结点小于父结点
            break;
    }
}
/* 插入新结点 */
void insert(int x)
{
    heap[++n] = x;      // 末尾插入新结点,并增加结点个数
    upAdjust(n, 1);
}

利用堆的性质进行排序。从末尾向前遍历堆数组,当访问第i个结点时,将其与堆顶元素进行交换,再在[1, i - 1]范围内对堆顶元素进行向下调整(相当于提取堆顶元素后将其删除,并调整堆),直到只剩第一个元素。此时剩下的数组已经排好序。

/* 堆排序 */
void heapSort()
{
    createHeap();
    for (int i = n; i > 1; i--)
    {
        swap(heap[1], heap[i]);
        downAdjust(1, i - 1);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值