堆的基本操作解析

堆排序
用堆封装优先级队列
海量数据TopK问题
堆的完整源代码

什么是堆

首先,堆是完全二叉树。
小堆: 任何一个节点的值都小于它的左右孩子的值,位于堆顶节结点的值是最小的,从根结点到每个结点的路径上数组元素组成的序列是递增的。
大堆:任何一个节点的值都大于它的左右孩子的值,位于堆顶节结点的值是最大的,从根结点到每个结点的路径上数组元素组成的序列是递减的。

堆的基本操作简单解析

建堆
  • 先申请一块空间,然后在对其赋值形成二叉树,在对二叉树调整使之形成小堆
  • 这里要注意,每次向下调整只是对一个元素调整,因此需要一个for循环。
//创建堆
void CreateHeap(Heap *hp, DataType *array, int size)
 {
    assert(hp);

    //申请空间
    hp->_array = (DataType *)malloc(sizeof(DataType)*size);
    if (NULL == hp)
        assert(0);
    hp->_size = 0;
    hp->_capacity = size;

    //赋值
    for (int i = 0; i < size; ++i)
    {
        hp->_array[i] = array[i];
        hp->_size += 1;
    }
    hp->_capacity = size;
    //找出最后一个非叶子节点
    int root = (size - 1 - 1) >> 1;

    //调整元素
    for (; root >= 0; root--)
        AdjustDown(hp,root);
}
向下调整算法(小堆为例)
  • 设该结点的下标为parent
  • 找到该结点的左孩子(child = (parent<<1)+1;)
  • 如果右孩子存在,则找出左右孩子中最小的孩子(建立大堆,则找出最大的孩子)
  • 比较parent和child的大小,如果parent小于child,则调整结束,否则,交换parent的child的值,此时如果其子树还不满足,则继续调整,直至子树也满足堆的性质
void AdjustDown(Heap  *hp, DataType parent)
{
    int child = (parent<<1)+1;

    if (NULL == hp)
        return;
    while (child < hp->_size)
    {
        //找到孩子中较小的一个
        if ((child+1) < hp->_size && 
            hp->cmp(hp->_array[child+1], hp->_array[child]))
        {
            child += 1;
        }
        //如果双亲大于孩子,则交换
        if (hp->cmp(hp->_array[child], hp->_array[parent]))
        {
            Swop(&hp->_array[parent], &hp->_array[child]);
            parent = child;
            child = (parent << 1) + 1;
        }
        else
        {
            break;
        }
    }
}
插入元素
  • 如果堆为空,返回
  • 检测容量,看是否还有空间,没有则扩容
  • 将元素添加到二叉堆的最后
  • 然后调整堆

void InsertHeap(Heap *hp, DataType data)
{
    if (NULL == hp)
        return;
    CheckCatacity(hp);
    hp->_array[hp->_size] = data;
    hp->_size++;

    if (hp->_size > 1)
    AdjustUp(hp, hp->_size - 1);
}
向上调整算法(插入元素)

在插入之前堆已经使最小堆了,因此插入元素之后,需要重新对堆进行调整。

  • 比较新插入的元素和parent的值的大小,如果大于,则返回,如果小于,则需要交换parent和child的值,然后继续比较,调整后的parent和他的parent的大小,直至满足小堆的性质
void AdjustUp(Heap *hp, int child)
{
    int parent = (child - 1) >> 1;

    while (child)
    {
        if (hp->cmp(hp->_array[child] , hp->_array[parent]))
        {
            Swop(&hp->_array[parent], &hp->_array[child]);
            child = parent;
            parent = (child - 1) >> 1;
        }
        else
            break;
    }
}
删除元素
  • 将堆中最后一个元素和堆顶元素交换
  • 将堆的元素个数减少一个,相当于删除堆中最后一个元素
  • 然后利用向下调整,使其满足最小堆的性质
void DeleteHeap(Heap *hp)
{
    assert(hp);
    if (NULL == hp)
        return;
    if (EmptyHeap(hp))
        return;

    Swop(&hp->_array[0], &hp->_array[hp->_size-1]);
    hp->_size--;
    AdjustDown(hp, 0);
}

猜你喜欢

转载自blog.csdn.net/lyjwonderful/article/details/80299963
今日推荐