zoukankan      html  css  js  c++  java
  • 堆--学习笔记(acwing)

    一:定义以及部分性质

    1:堆中某个节点的值总是不大于或不小于其父节点的值;

    2:堆总是一棵完全二叉树。

    3:最后一个非叶结点是第n/2个结点。这里建堆的时候会用到,复杂度可以降到O(n);

    二:关于建堆

    如果一个点为u,那么它的左儿子为2*u,右儿子为2*u+1

    对于小根堆来讲,每个以u为根节点的树,它的左右儿子的值一定大于父节点。

    所以对堆进行建立的时候,如果当前点与它的左右儿子并不满足这一性质,就需要进行交换。

    对于建堆的过程:

    由(一)第三条,我们建堆的时候,只需要从n/2开始,到1即可。因为已知最后一个非叶结点是n/2,那么>n/2的结点,都是叶子结点,它本身所构成的子树只有它本身。一定符合小根堆性质,所以是没必要看的。我们从第n/2结点开始,每次让它向下比较一次,变为局部的合法(比如第一次只有三个点,满足父节点都小于左右儿子结点)。然后它的向上的父节点再向下比较,那么每次比较,都会得到一个局部合法的子树(本次进行的父节点往下的子树)。所有的n/2~1都这么操作,一定可以把整个完全二叉树变成合法的小根堆。

    有:

        for(int i=n/2;i>=1;i--)
        {
            down(i);
        }

    对于向下比较的过程,我们定为down(u)函数。由小根堆的性质,u为父节点,那么它的左右儿子h[2*u]和h[2*u+1]都要大于它,否则就要进行交换:

    void down(int u)
    {
        //t表示三个点中的最小值 
        int t=u;
        //判断是否有左儿子
        if(u*2<=siz&&h[2*u]<h[t]) 
        {
            t=2*u;
        }
    //是否有右儿子
    if(2*u+1<=siz&&h[2*u+1]<h[t]) { t=2*u+1; } if(u!=t) {
         //需要交换,则交换,继续down往下判断 swap(h[u],h[t]); down(t); } }

    只要down()就可以把堆给建好了。

    关于up操作:

    比down更简单,只需要与它的父节点u/2进行比较即可,非法即交换。

    void up(int u)
    {
        while(u/2>=1&&h[u/2]>h[u])
        {
            swap(u,u/2);
            u=u/2;
        }
    }

    三:堆的操作

    定siz为树大小

    1:插入一个数:

    直接在末尾加一个,所以++size处插入新数,然后进行更新树。由于此时它处于树的最底部,所以直接up(size)即可。

    2:求集合中最小值:

    h[1]

    3:删除最小值:

    把树尾覆盖上去,siz--,由于此时它处于树的顶部,所以直接down(1)即可。

    4:删除任意一个元素:

    删除K号结点。与3类似,直接把树尾覆盖上去,siz--,但是需要up(k)或down(k)一遍,为了简便,把up和down都写上,只会执行其中一个。

    5:修改任意一个元素

    直接修改h[k]=x,up(k),down(k)

    四:堆排序

    堆的其中一个作用,堆排序:时间复杂度O(NlogN)

    板子题:https://www.acwing.com/problem/content/description/840/

    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<set>
    #include<algorithm>
    #include<iostream>
    #include<vector>
    using namespace std;
    typedef long long ll;
    const int maxn=1e5+10,maxn2=31*maxn;
    int n,m;
    int h[maxn],siz;//1~n
    void down(int u)
    {
        //t表示三个点中的最小值 
        int t=u;
        //判断是否有左儿子
        if(u*2<=siz&&h[2*u]<h[t]) 
        {
            t=2*u;
        }
        if(2*u+1<=siz&&h[2*u+1]<h[t])
        {
            t=2*u+1;
        }
        if(u!=t)
        {
            swap(h[u],h[t]);
            down(t);
        }
    }
    int main()
    {
        cin>>n>>m;
        for(int i=1;i<=n;i++)
        {
            cin>>h[i];
        }
        siz=n;
        for(int i=n/2;i>=1;i--)
        {
            down(i);
        }
        while(m--)
        {
            cout<<h[1]<<" ";
            h[1]=h[siz]; //更新最小值
            siz--;
            down(1);
        }
        return 0;
    }

    vector排序版:

    #include<cstdio>
    #include<cstring>
    #include<vector>
    #include<set>
    #include<algorithm>
    #include<iostream>
    #include<vector>
    using namespace std;
    typedef long long ll;
    const int maxn=2e5+10,maxn2=31*maxn;
    const int inf=1e18+10;
    ll a[maxn],b[maxn];
    int main()
    {
        int n,m;
        vector<int>v;
        cin>>n>>m;
        int x;
        for(int i=1;i<=n;i++)
        {
            cin>>x;
            v.push_back(x);
        }
        sort(v.begin(),v.end());
        for(int i=0;i<m;i++)
            cout<<v[i]<<" ";
            cout<<endl;
        return 0;
    }

    五:堆操作的基本实现:

    地址:https://www.cnblogs.com/liyexin/p/13942227.html

  • 相关阅读:
    Hibernate优缺点
    Struts优缺点
    Problem M
    Problem K
    Problem K
    Problem Q
    Problem Q
    Problem F
    Problem F
    哈夫曼树
  • 原文地址:https://www.cnblogs.com/liyexin/p/13942174.html
Copyright © 2011-2022 走看看