数据结构-二叉堆

表示形式

堆是一个数组,可以被看成是一个近似的完全二叉树。表示堆的数组A包含两个属性:A.length表示元素个数,A.heap-size表示有多少个堆元素存储在数组中。

也就是说,虽然A[1...A.length]可能都存有数据,但是只有A[1...A.heap-size]中存放的是堆的有效元素(0 <= A.heap-size <= A.length)。

数组元素下标从0开始,可以使用宏来定义左右子节点和父节点:

#define LEFT(idx) (((idx)<<1)+1)
#define RIGHT(idx) (((idx)<<1)+2)
#define PARENT(idx) (((idx)-1)>>1)

 

这种表示的是二叉堆。

二叉堆可以分两种形式:最大堆、最小堆。

其中,最大堆指:除了根节点外,所有节点 i 都要满足 A[PARENT(i)] >= A[i],堆中最大元素在根节点,并且,任一子树中,该子树包含的所有节点的值,都不大于该子树根节点的值。

最小堆相反,指:除根节点外,所有节点 i 都要满足 A[PARENT(i)] <= A[i],堆中最小元素在根节点,并且,任一子树中,该子树包含的所有节点的值,都不小于该子树根节点的值。

维护堆的性质

堆的操作分为“下沉”和“上浮”

以最大堆为例,下沉方法是:假设某个节点的左右子树都是最大堆了,拿该节点的值和左右子树值对比,取最大的一个(比如是左节点)和该节点交换,这样,这个节点的值就是这个树中最大的了。但是,这样可能破坏左子树的堆性质,于是,继续对左子树根节点执行“下沉”操作。

“上浮”方法是:假设下标小于该节点的元素,已经构成了最大堆,调节此元素时,与父节点比较,如果大于父节点的值,交换,这样父节点大于此节点的值了。但是,这样可能破坏了祖先节点的堆性质,所以,继续对祖先节点执行“上浮”操作

// 下沉
void sink(int* arr, int index, int heapsize)
{
    // 左子树节点超出堆长度范围
    if (LEFT(index) > heapsize-1)
    {
        return;
    }

    int maxidx = -1;
    // 右子树节点超出堆长度范围,或者左节点的值比右节点的值大
    if (RIGHT(index) > heapsize-1 || (arr[LEFT(index)] > arr[RIGHT(index)]))
    {
        maxidx = LEFT(index);
    }
    else
    {
        maxidx = RIGHT(index);
    }

    if (maxidx != -1 && arr[maxidx] > arr[index])
    {
        int tmp = arr[maxidx];
        arr[maxidx] = arr[index];
        arr[index] = tmp;
        sink(arr, maxidx, heapsize);
    }
}

// 上浮
void upward(int* arr, int index, int heapsize)
{
    if (0 == index)
    {
        return;
    }

    if (arr[PARENT(index)] < arr[index])
    {
        int tmp = arr[index];
        arr[index] = arr[PARENT(index)];
        arr[PARENT(index)] = tmp;
        upward(arr, PARENT(index), heapsize);
    }
}

 

建堆

把一个普通数组调整成为一个堆的过程。有了下沉和上浮的方法,我们可以对每个元素执行这样的操作,实现建堆。

void adjust_by_sink(int* arr, int len)
{
    // 下沉法,从最后一个非叶子节点开始,到根节点,依次执行下沉
    for (int i = PARENT(len-1); i >= 0; i--)
    {
        sink(arr, i, len);
    }
}

void adjust_by_upward(int* arr, int len)
{
    // 上浮法,从根节点到最后一个节点,依次执行上浮
    for (int i = 0; i < len; i++)
    {
        upward(arr, i, len);
    }
}

 

插入元素

把元素插入最后一个位置,然后,让这个节点上浮,调整为一个堆

int insert(int* arr, int value, int& heapsize)
{
    if (heapsize > ARRLEN)
    {
        return -1;
    }

    arr[heapsize] = value;
    upward(arr, heapsize, heapsize+1);
    heapsize++;

    return 0;
}

 

删除元素

堆的删除元素,一般指删除第一个元素,因为第一个是需要的最大值或者最小值。

通过把最后一个值赋值给根节点,然后,把根节点下沉,再次调整为一个新的堆

void del(int* arr, int& heapsize)
{
    if (heapsize <= 0)
    {
        return;
    }

    arr[0] = arr[heapsize-1];
    heapsize--;
    sink(arr, 0, heapsize);
}

 

 

posted @ 2018-09-17 23:22  二狗啸地  阅读(284)  评论(0编辑  收藏  举报