【数据结构】堆

1.堆的概念
如果有一个关键码的集合K = {k0,k1, k2,…,kn-1},把它的所有元素按完全二叉树的顺序存储方式存储在一个一维数组中,并满足:Ki <= K2i+1 且 Ki<=K2i+2 ,则称为小堆(或大堆)。将根节点最大的堆叫做最大堆或大根堆,根节点最小的堆叫做最小堆或小根堆。完全二叉树(除了最后一层以外上面的节点但是非空的,最后一层节点是从左到右依次排布的)。如图:

2.堆的性质
1.堆中某个节点的值总是不大于或不小于其父节点的值;
2.堆总是一棵完全二叉树。

2.堆的存储
   堆的存储不同于链表的存储,堆的存储是用一个一维数组来存储的。以小根堆为例,根节点为最小值,小于它的左儿子和右儿子。根节点x存储在小标为1的数组里,左儿子存储在下标为2x的数组,右儿子存储在下标为2x+1的数组里。

3.堆的基本操作(小根堆为例)
   堆的两个基本操作为down操作和up操作,顾名思义,down操作就是向下调整,up操作就是向上调整,这两个操作即可以完成以上的五个问题。

     down操作如图所示移动,因为我将根节点或者某一点改变后,这一点比它的儿子点大了,所以我们要将这个点向下移动,直到重新成为一个堆。

   up操作,和down操作相反,up操作是将一个点的值变小,然后不断向上移动直到成为一个新的堆。同样如图:

 4.如何手写一个堆(小根堆)
     1)插入一个数:插入一个数是在数组的最后面插入,然后不断往上移,heap[++size]=x,up(size);

     2)求集合中的最小值:数组第一个数一定是最小的;heap[1];

     3)删除最小值:  删除最小值需要一定技巧,因为堆是一个一维数组,删除第一个比较困难,所以将数组的最后一个覆盖到第一个数,然后将最后一个数删除,最后在down操作一遍;heap[1]=heap[size],size--,down(1);

     4)删除任何一个元素: 和删除根节点是类似的,假如是第k的点那么就是让最后一个数等于第k个数,这里需要判断一下看看改变后的k是变大了还是变小了,我们也可以不进行判断直接down操作一遍up操作一遍,因为无外乎只有3种情况变大,变小或者不变,所以down和up只会选择一个。heap[k]=heap[size],size--,down(k),up(k);

     5)修改任何一个元素: 修改一个元素和删除一个元素同理,我们修改完以后down操作一遍up操作一遍就可以了。heap[k]=x,down(k),up(k);

5.堆的模板
  down模板:

#include<bits/stdc++.h>
using namespace std;
const int N=100010;
int h[N],siz;
int n,m;
void down(int x)
{
    int t=x;
    if(2*x<=siz && h[2*x]<h[t]) t=2*x;
    if(2*x+1<=siz && h[2*x+1]<h[t]) t=2*x+1;
    if(x!=t)
    {
        swap(h[x],h[t]);
        down(t);
    }
}

up模板:

void up(int x)
{
    while(x/2 &&h[x]<h[x/2])
    {
        headswap(x/2,x);
        x/=2;
    }

 6.数据结构中堆和栈的不同
堆和栈在数据结构中是两种不同的数据结构。 两者都是数据项按序排列的数据结构。

   栈:像是装数据的桶或者箱子

 栈是大家比较熟悉的一种数据结构,它是一种具有后进先出的数据结构,也就是说后存放的先取,先存放的后取,这就类似于我们要在取放在箱子底部的东西(放进去比较早的物体),我们首先要移开压在它上面的物体(放入比较晚的物体)。

   堆:像是一颗倒立的大树

   堆是一种经过排序的树形数据结构,每个节点都有一个值。通常我们所说的堆的数据结构是指二叉树。堆的特点是根节点的值最小(或最大),且根节点的两个树也是一个堆。由于堆的这个特性,常用来实现优先队列,堆的存取是随意的,这就如同我们在图书馆的书架上取书,虽然书的摆放是有顺序的,但是我们想取任意一本时不必像栈一样,先取出前面所有的书,书架这种机制不同于箱子,我们可以直接取出我们想要的书。

所以堆要实现的是按照优先权(关键字)的顺序来排序,取出。


 

1.不是完全二叉树2.不是完全二叉树3.不符合最大堆或者最小堆4.同3

有序就是从大到小或者从小到大。

 

 

+1是因为这个堆的存储是从数组下标为1开始存储的,要容纳MaxSize这么多,就需要+1.

然后下标为1的地方开始放置,0不管,所以当前元素个数是0,该函数中创建了一个数组,在数组中存放完全二叉树来表述堆,下标0处用存的是哨兵。

 

思路就是插入结点一般就是插入最后嘛,然后跟父节点比较,如果大于父节点,就跟父节点交换位置,然后再次与父节点比较,直到在树中呈现一定顺序即可。

 i/2是父节点

如果要插入的值比所有父节点都大,会一直比较,直到哨兵处,与哨兵比较,因为哨兵的值比元素中所有元素的值都大,所以这个循环就会止步在下标等于1的地方,也可以通过设定i>1才进行比较也可以,不过哨兵的话复杂度会简单一点。

 

 

 

 

过程就是用数组最后的元素放在删除位置,然后与子孩子比较,与大的交换位置,交换之后再与孩子结点比较,然后交换位置与否。

 

 删除元素的值会被返回出去,所以要保存

右儿子需要+1,没左肯定没右儿子

表示child不是最后一个元素,即存在右儿子。

 

如果放上去的元素比儿子中最大的还要大,位置就坐牢了,退出,否则两者交换位置,进入循环的下一轮。

 

 

 

 

 思想就是从倒数第一个有孩子的结点开始不断地循环建成堆,怎么建呢?就是不断跟孩子节点比较,如果父节点比孩子节点大,就不用换,否则换位,一直与孩子节点比较,直到它所在位子比孩子节点大或者称为叶节点为止。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值