(数据结构)堆

Posted pisceskkk

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了(数据结构)堆相关的知识,希望对你有一定的参考价值。

简介

堆,一般通过二叉树实现,且保证根节点优先级总是高于(或低于)两个子节点。
二项式堆、斐波那契堆见补充部分。

基本性质

  1. 根节点优先级总是高于(或低于)两个子节点。
  2. 对于节点\(i\),其两个子节点分别是 \(i*2\)\(i*2+1\),即 \(i<<1\)\(i<<1|1\)
  3. 为了简洁,令heap[0]作为 heap_size 存储 堆的大小。

功能

  1. 堆排序(直接在乱序 heap[] 数组基础上)—— heapsort
  2. 清除堆 —— clear
  3. 插入数据 —— push
  4. 取根 —— top
  5. 删除根 —— pop
  6. 更新,为了维护某一节点的性质 —— update

代码实现

//////////////////////////////////////////////////////////////////////
//Target: 洛谷 P3378 【模板】堆
//@Author: Pisceskkk
//Date: 2019-5-6
//////////////////////////////////////////////////////////////////////
#include<cstdio>
#include<cstring>
#define N 1000010
using namespace std;

int n,heap[N];    //小根堆

void update(int x){
    while(x > 1 && heap[x>>1] > heap[x]){    // 不要让x更新到1!
        heap[x>>1] ^= heap[x] ^= heap[x>>1] ^= heap[x];
        x >>= 1;
    }
}

void heapsort(int len){
    heap[0] = len;
    for(int i=1;i<len;i++){
        update(i);
    }
}

void clear(){
    memset(heap,0x7f,sizeof(heap));
    heap[0]=0;
}

void push(int x){
    heap[++heap[0]] = x;
    update(heap[0]);
}

int top(){
    return heap[1];
}

void pop(){
    int x=1;
    heap[1] = heap[heap[0]];    //取最后一个元素补上去,然后维护。这样可以保证heap[0] 位置可以删掉。
    heap[0] --;
    while((x<<1) <= heap[0]){
        x = (heap[x<<1] < heap[x<<1|1]) ? (x<<1) : (x<<1|1);
        if(heap[x] < heap[x>>1])heap[x>>1] ^= heap[x] ^= heap[x>>1] ^= heap[x];
    }
}

int main(){
    scanf("%d",&n);
    clear();
    int x;
    while(n--){
        scanf("%d",&x);
        if(x == 1){
            scanf("%d",&x);
            push(x);
        }
        else if(x == 2){
            printf("%d\n",top());
        }
        else if(x == 3){
            pop();
        }
    }
    return 0;
}

补充

以下部分大多来自维基百科

二项堆

二项堆更方便实现两个堆的合并。

其递归定义如下:

  • 度数为0的二项树只包含一个结点
  • 度数为k的二项树有一个根结点,根结点下有 \(k\)个子女,每个子女分别是度数分别为 \(k-1,k-2,...,2,1,0\) 的二项树的根

图示(来源自维基百科):
技术图片

度数为 \(k\) 的二项树共有 \(2^{k}\)个结点,高度为 \(k\)。在深度d处有 \(\tbinom {k}{d}\)(二项式系数)个结点。
度数为k的二项树可以很容易从两颗度数为k-1的二项树合并得到:把一颗度数为k-1的二项树作为另一颗原度数为k-1的二项树的最左子树。这一性质是二项堆用于堆合并的基础。

实现详见二项堆的操作

斐波那契堆

简介

斐波那契堆(Fibonacci heap)是计算机科学中树的集合。它比二项堆具有更好的平摊分析性能,可用于实现合并优先队列。不涉及删除元素的操作有O(1)的平摊时间。 Extract-Min和Delete的数目和其它相比,较小时效率更佳。稠密图每次decrease key只要O(1)的平摊时间,和二项堆的O(lg n)相比是巨大的改进。
斐波纳契堆于1984年由Michael L. Fredman与Robert E. Tarjan提出,1987年公开发表。[1]名字来源于运行时分析使用的斐波那契数。

结构

斐波那契堆是由一组最小堆有序树构成的。每个节点的度数为其子节点的数目。树的度数为其根节点的度数。
斐波那契堆中的树都是有根的但是无序。每个节点x包含指向父节点的指针p[x]和指向任意一个子结点的child[x]。x的所有子节点都用双向循环链表链接起来,叫做x的子链表。子链表中的每一个节点y都有指向它的左兄弟的left[y]和右兄弟的right[y]。如果节点y是x仅有的子节点,则left[y]=right[y]=y。
斐波那契堆中所有树的根节点也用一个双向循环链表链接起来。
使用一个指针指向斐波那契堆中最小元素。

实现

详见斐波那契堆操作

以上是关于(数据结构)堆的主要内容,如果未能解决你的问题,请参考以下文章

是否可以在 OnStop() 回收所有数据并使用保留片段?

算法排序之堆排序

是否可以在OnStop()上回收所有数据并使用保留片段?

Rail片段缓存如何使您的应用受益,即阻止数据库调用?

方法与对象内存分析

VSCode自定义代码片段5——HTML元素结构