堆的实现.

         这是C++算法基础-数据结构专栏的第三十二篇文章,专栏详情请见此处


引入

        今天我们要学习的是堆,这个数据结构可以在存储的时候维护一些数据的大小关系,它的重要用途是堆排序。

        下面我们就来讲堆的实现。

定义

        堆,一般指二叉堆,它是一棵完全二叉树,分为大根堆和小根堆。小根堆中,父亲节点的值不大于儿子节点的值,根节点的值最小,大根堆反之。堆重要用途是堆排序。

过程

        这里我们用讲解小根堆来理解堆,大根堆的过程和代码可通过小根堆得出,不再详细讲解。

        存储

        对于堆的存储,我们可以用一个数组h[]存储,若父亲节点编号为n,则它的左右儿子节点分别为2n2n+1。此外,还需要一个变量siz表示根数组的大小。

        这种用一维数组存储,通过数组下标辨识父亲节点和儿子节点的方式是存储树很常见也很方便的方式。

        从定义中我们得知,小根堆中,不合法的情况是父亲节点的值大于儿子节点的值,对于此种情况,我们可以用两种方式处理:一是将父亲节点下移,二是将儿子节点上移。这两者虽然看上去区别不大,但侧重点不同。

        移动函数:down()和up()

        父亲节点下移我们用一个函数down()。首先用一个变量t存储三个结点(该父亲节点和它的左右儿子节点)中存在的最小的结点的下标,初始化为当前结点u;然后,若左右儿子节点存在并且小于t,就依次更新t,最后t就是三个结点中拥有最小值的结点下标;接着,若t\neq u,则说明需要交换数值,交换后父亲节点就成功下移了;最后,交换数值后,t存储原来u的值,可能需要调整,所以继续调用down()函数,直到它比左右儿子节点都小。

        儿子节点上移我们用一个函数up()up()函数比down()函数实现简单,因为父亲节点最多有两个儿子节点,但儿子节点只有一个父亲节点,所以我们只需判断,若当前节点不是根节点且当前节点的父亲节点大于它,则交换数值;最后,交换后可能需要调整,所以继续调用函数up()

        

        有了上面两个函数,操作堆就方便多了。堆的常见操作有五个:插入数据,求最小值,删除最小值,删除元素和修改元素。我们依次讲解上述操作。

        插入数据

        插入的时候,我们一般在数组最后插入数据,然后将其上移

        求最小值

        由于小根堆中,根节点最小的缘故,只需输出根节点即可。

        删除最小值

        删除最小值并不是把根节点删除,而是需要先把根节点和位置最后的节点交换,再将siz--,最后将其下移

        删除元素

        和删除最小值差不多,都是先把该节点和位置最后的节点交换,再siz--,唯一不同的是需要down()up()都调用,因为我们不知道它与其父亲节点和儿子节点的关系。

        修改元素

        重新赋值,然后down()up()

代码

        下面给出堆的实现代码:

        注:operation 操作

int h[N],siz;

void down(int u){
    int t=u;
    if(u*2<=siz&&h[u*2]<h[t])
		t=u*2;
    if(u*2+1<=siz&&h[u*2+1]<h[t])
		t=u*2+1;
    if(u!=t){
        swap(u,t);
        down(t);
    }
}

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

//operation 1
h[++siz]=x;
up(siz);

//operation 2
cout<<h[1];

//operation 3
h[1]=h[siz];
siz--;
down(1);

//operation 4
h[k]=h[siz];
siz--;
up(k);
down(k);

//operation 5
h[k]=x;
up(k);
down(k);
        代码解释

        第一行是存储堆的数组和该数组的长度;down()函数的作用是将父亲节点下移,up()函数的作用是将儿子节点上移;第一个操作的作用是插入数据;第二个操作的作用是求最小值;第三个操作的作用是删除最小值;第四个操作的作用是删除元素;第五个操作的作用是修改元素。


上一篇-并查集的实现(带权版)与食物链(NOI2001)问题    C++算法基础专栏文章    下一篇-堆排序的实现


每周六更新一篇文章,内容一般是自己总结的经验或是在其他网站上整理的优质内容

点个赞,关注一下呗~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值