数据结构-Heap(堆)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/creatorx/article/details/80215543

好久没有写博客了,感觉学的东西都快忘完了。这几天准备学下平衡树的知识,不过在学习那之前还是先学习一下更为基础的一种数据结构Heap,我们称为堆。

先说下什么叫做堆,堆有小根堆和大堆根,首先堆的本质是一棵完全二叉树。跟二叉搜索树的区别在于,二叉搜索树的特点是左子树上的所有节点的值都小于根节点的值,右子树上的所有节点的值都大于根节点的值。然后对于每棵子树,都是一棵二叉搜索树。根据二叉搜索树的定义我们容易知道,对于任意一个节点root,以这个节点的根的左子树的任意一节点记为l,以这个节点为根的右子树的任意一节点记为r,那么我们肯定有value(r) >= value(root)>= value(l).而我们今天要讲的堆的特点是(以大根堆为例,小根堆类似),对于任意一节点,每个节点的值都大于其左右儿子节点的值。所以以每个节点为根的子树都是堆,这一点跟二叉搜索树一样(二叉搜索树的每个节点为根的子树都是一棵二叉搜索树),但是与二叉搜索树不同的是,对于对于左右子树上的节点的值没有大小关系,只要他们都小于根的值就行。

知道什么叫作堆之后,那么这个堆有什么用呢?。堆的用处很多,比如我们stl里面的优先队列就是基于堆原理实现的,另外利用堆还可以进行排序,就是著名的堆排序等等。有很多作用。

今天我们用一个具体的问题来学习一下堆。

#1105 : 题外话·堆

时间限制:10000ms
单点时限:1000ms
内存限制:256MB

描述

小Ho有一个糖果盒子,每过一段时间小Ho都会将新买来的糖果放进去,同时他也会不断的从其中挑选出最大的糖果出来吃掉,但是寻找最大的糖果不是一件非常简单的事情,所以小Ho希望能够用计算机来他帮忙计算这个问题!

提示:吃糖果吃多了会变胖的!

输入

每个测试点(输入文件)有且仅有一组测试数据。

在一组测试数据中:

第1行为1个整数N,表示需要处理的事件数目。

接下来的M行,每行描述一个事件,且事件类型由该行的第一个字符表示,如果为'A',表示小Ho将一粒糖果放进了盒子,且接下来为一个整数W,表示这颗糖果的重量;如果为'T',表示小Ho需要知道当前盒子中最重的糖果的重量是多少,在知道这个值之后,小Ho会将这颗糖果从盒子中取出并吃掉。

对于100%的数据,满足1<=N<=10^5, 1<=w<=10^5。<>

对于100%的数据,满足没有2颗糖果的重量是相同的,最开始的时候小Ho的糖果盒子是空的,且每次小Ho想要取出一颗糖果的时候盒子里一定至少有一颗糖果。

输出

在一组测试数据中:

对于每个类型为'T'的时间,输出1个整数W_MAX,表示在这一时刻,盒子中最重的糖果的重量。


样例输入
5
A 77751
A 1329
A 26239
A 80317
T
样例输出
80317

题意一目了然。显然我们可以用stl的优先队列,或者离线和离散化之后用线段树或者树状数组做,都是ok的。但是我们今天就是要用堆来做。

显然我们需要建一个完全二叉树来作为堆。显然这道题目是建一个大根堆,那么整棵树的根就是我们要找的最大值。我们需要实现两种操作。一种操作是取出最大值也就是整棵树的根元素,这个很简单,但是取出之后还要将这个最大元素删掉。另一种操作是向树中插入一个数。下面分别来简述两种操作的实现原理

1.取最大值,并删除它。

对于取最大值,很简单,直接保存一下就行。对于删除操作。我们用二叉树的最后一个叶节点来覆盖根节点,然后删除最后一个 叶节点(代码实现就行让这棵树的大小减一就行)。但是这样做了之后根的值就变了,可能就不满足大根堆的性质了。所以这个时候我们需要做一些调整,使得它满足大根堆的性质。怎样调整?很简单,从根节点开始,找出节点的两个儿子,然后与值较大的那个儿子值交换就行。然后继续调整刚才交换的那个儿子的那一边就行。(另一边一定是满足大根堆的性质的)。然后到叶节点或者该节点的值大于左右儿子的值时就停止,这时候整棵树就调整为大根堆了。这个操作是从根节点往下,所以也称下称操作。


2,在树种插入一个数。

我们直接在树的最后一个叶节点后面加一上这个树。加上之后,也有可能会使这棵树不满足大根堆的性质,然后我们也需要调整一下。调增的方法与上面类似。我们从这个节点开始。找到这个节点的父节点,然后判断这个节点的值与父节点值的大小关系,如果大于父节点的值,则与父节点的值交换,继续调整父节点就行,知道小于父节点或者到了整棵树的根结束。这个操作是从叶节点往上,所以叶称为上浮操作。

时间复杂度分析,非此插入操作,最坏是从叶节点到根节点即为树的高度log(n),删除根节点复杂度也一样。所以最后的复杂度为O(m * logn) m为操作次数,n的节点的个数,也就是插入的数的个数。

代码如下:

#include <bits/stdc++.h>
using namespace std;
const int maxn = 100000 + 10;
int N;
struct node{
    int value;
    int L, R;
    node(){
        L = 0;
        R = 0;
    }
};
class Heap{
public:
    Heap()
    {
        root = 0;
        cnt = 0;
    }
    void insert(int value)//插入一个值
    {
        if(root == 0)//空树
        {
            root = ++cnt;
            Tree[root].value = value;
            Tree[root].L = 0;
            Tree[root].R = 0;
        }
        else
        {
           int father = cnt>>1;
           if(Tree[father].R == 0 && father)
           {
               Tree[father].R = ++cnt;
               Tree[cnt].L = 0;
               Tree[cnt].R = 0;
               Tree[cnt].value = value;
           }
           else
           {
               father++;
               Tree[father].L = ++cnt;
               Tree[cnt].L = 0;
               Tree[cnt].R = 0;
               Tree[cnt].value = value;
           }
        }
    }
    void update_up(int id)//丛编号为id的节点开始向上调整,使得整棵树满足大根堆的性质
    {
        int father = id>>1;
        if(father == 0) return;
        if(Tree[father].value < Tree[id].value)
        {
            int temp = Tree[father].value;
            Tree[father].value = Tree[id].value;
            Tree[id].value = temp;
            update_up(father);
        }
        else return;
    }
    int getRoot()//获取整棵树的根,也就是最大值
    {
        int temp = Tree[root].value;
        Tree[root].value = Tree[cnt].value;
        int father = cnt>>1;
        if(cnt == Tree[father].L) Tree[father].L = 0;
        else Tree[father].R = 0;
        cnt--;
        return temp;
    }
    void update_down(int id)//从根往下更新,直到整棵树满足大根堆性质
    {
        int lson = Tree[id].L;
        int rson = Tree[id].R;
        if(lson == 0 && rson == 0) return;
        if(rson == 0)
        {
            if(Tree[id].value < Tree[lson].value)
            {
                int temp = Tree[id].value;
                Tree[id].value = Tree[lson].value;
                Tree[lson].value = temp;
                update_down(lson);
                return;
            }
            else return;
        }
        if(Tree[lson].value >= Tree[rson].value)
        {
            if(Tree[id].value >= Tree[lson].value) return;
            int temp = Tree[id].value;
            Tree[id].value = Tree[lson].value;
            Tree[lson].value = temp;
            update_down(lson);
            return;
        }
        else
        {
            if(Tree[id].value >= Tree[rson].value) return;
            int temp = Tree[id].value;
            Tree[id].value = Tree[rson].value;
            Tree[rson].value = temp;
            update_down(rson);
            return;
        }
    }
    node Tree[maxn];
    int root;//树根
    int cnt;
};
int main()
{
    scanf("%d", &N);
    Heap x;
    char op[10];
    for(int i = 1; i <= N; i++)
    {
        scanf("%s", op);
        if(op[0] == 'T')
        {
            printf("%d\n", x.getRoot());
            x.update_down(x.root);
        }
        else
        {
            int value;
            scanf("%d", &value);
            x.insert(value);
            x.update_up(x.cnt);
        }
    }
    return 0;
}

展开阅读全文

没有更多推荐了,返回首页