堆结构的细节处理(向上调整与向下调整)

堆-完全二叉树

堆适合使用数组存储

堆分为大堆与小堆

大堆:数中一个数及子树中,他父亲都大于等于孩子

小堆:数中一个数及子树中,他父亲都小于等于孩子

  1. 堆排序(效率高)

  1. topK问题。在N个数中,找出最大(小)的前K个

堆结构的细节处理(向上调整与向下调整)_第1张图片

逻辑结构: 我们自己现象的 ----完全二叉树

物理结构:在内存中实实在在存储的结构----数组

经典错误:向上调整错误

堆结构的细节处理(向上调整与向下调整)_第2张图片

假设:对已经建堆的结构插入数据9的结点;

堆结构的细节处理(向上调整与向下调整)_第3张图片
堆结构的细节处理(向上调整与向下调整)_第4张图片

插入数据九在堆结构的末尾(push)

堆结构的细节处理(向上调整与向下调整)_第5张图片
堆结构的细节处理(向上调整与向下调整)_第6张图片

第一趟向上调整

if(a[child]>a[parent]);//9>3成立
//交换后迭代
//迭代后child==2  parent==0;

调整后

堆结构的细节处理(向上调整与向下调整)_第7张图片

此时parent==0

while(parent>=0);循环条件成立,继续循环。

第二趟向上调整

if(a[child]>a[parent]);//9>5成立
//交换后迭代
//迭代后child==0  parent==0;

调整后:

堆结构的细节处理(向上调整与向下调整)_第8张图片

while(parent>=0);循环条件成立,继续循环。

第三趟向上调整

if(a[child]>a[parent]);//5>5不成立
//进入分支
else//进入else
break;//中断循环。

这里会发现循环多了一次,这里虽然产生了bug但是程序依旧成功

如何更改?

观察程序,会发现当child等于0时,parent也为0就应该结束了,所以改变循环条件

 while (parent>=0)//改为孩子判断
{}
 while(child)
{}

完整接口代码:

void AdjustUp(int* a, int child)
{
    assert(a);
    int parent = (child - 1) / 2;
    while (child)//判断此孩子是否是
    {
        //这里的大小与号决定大小堆  大于决定大堆 孩子比父亲大,交换位置  小于决定小堆 孩子比父亲小,交换位置
        if (a[child] < a[parent])
        {
            Swap(&a[child], &a[parent]);
            child = parent;
            parent = (child - 1) / 2;
        }
        else
        {
            break;
        }

    }
}

注意:所有数组可以表示完全二叉树,但是不一定是堆

大堆:树中要求所有的父亲要大于等于孩子

小堆:树中要求所有的父亲要小于等于孩子

删除堆顶数据

会发现如果单纯的删除根节点,用数组向前覆盖所有数据向前挪动一位;

堆的结构全部被打乱了

堆结构的细节处理(向上调整与向下调整)_第9张图片

将根结点删除首先根结点

堆结构的细节处理(向上调整与向下调整)_第10张图片

会发现结构大改变,里面的数据会发生错位。得出结论这样是不行的,所以我们要转变思路。

交换根和尾子叶数据

交换尾子叶数据,然后,将数组有效数据size--,再使用向下调整函数

堆结构的细节处理(向上调整与向下调整)_第11张图片

交换:

堆结构的细节处理(向上调整与向下调整)_第12张图片

减少有效数据

堆结构的细节处理(向上调整与向下调整)_第13张图片

开始进入向下调整函数,

先比较根节点的左右节点数据,

  1. 如果左树存在,右树也存在,就比较左树和右树的数据大小,大的和根比较,大于根就交换,如何否则结束循环。

        if (childRight < sz)
        {
            //这里的大小与号决定大小堆  大于决定大堆 孩子比父亲大,交换位置  小于决定小堆 孩子比父亲小,交换位置
            int childmin = a[childLeft] > a[childRight] ? childLeft : childRight;
            if (a[childmin] > a[parent])
            {
                Swap(&a[childmin], &a[parent]);
                parent = childmin;
                childLeft = parent * 2 + 1;
                childRight = parent * 2 + 2;
            }
            else
            {
                break;
            }
  1. 如果左树存在,右树不存在,就只用比较左树和根的数据大小,大于根就交换,如何否则结束循环。

        else
        {
            int childmin = childLeft;
            if (a[childmin] > a[parent])
            {
                Swap(&a[childmin], &a[parent]);
                parent = childmin;
                childLeft = parent * 2 + 1;
                childRight = parent * 2 + 2;
            }
            else
            {
                break;
            }
        }

3.如果左树不存在直接结束循环

    while (childLeft < sz)
   {}

交换后

堆结构的细节处理(向上调整与向下调整)_第14张图片

第二次循环发现3为根之后无子树,结束循环

完整代码为

void Swap(HPDataType* px,HPDataType*py)
{
    HPDataType tmp = *px;
    *px = *py;
    *py = tmp;
}

void adjustDown(HPDataType* a, size_t sz, int parent)
{

    int childLeft = parent * 2 + 1;
    int childRight = parent * 2 + 2;
    while (childLeft < sz)
    {
        if (childRight < sz)
        {
            //这里的大小与号决定大小堆  大于决定大堆 孩子比父亲大,交换位置  小于决定小堆 孩子比父亲小,交换位置
            int childmin = a[childLeft] > a[childRight] ? childLeft : childRight;
            if (a[childmin] > a[parent])
            {
                Swap(&a[childmin], &a[parent]);
                parent = childmin;
                childLeft = parent * 2 + 1;
                childRight = parent * 2 + 2;
            }
            else
            {
                break;
            }
        }
        else
        {
            int childmin = childLeft;
            if (a[childmin] > a[parent])
            {
                Swap(&a[childmin], &a[parent]);
                parent = childmin;
                childLeft = parent * 2 + 1;
                childRight = parent * 2 + 2;
            }
            else
            {
                break;
            }
        }

    }
}



//删除堆顶的数据
void HeapPop(HP* hp)
{
    assert(!HeapEmpty(hp));
    Swap(&hp->a[0], &hp->a[hp->size - 1]);
    hp->size--;
    adjustDown(hp->a, hp->size, 0);
}

你可能感兴趣的:(c++,算法,数据结构)