数据结构堆的性质、操作的实现,以及个人对堆的见解

堆在stl对应的是优先队列,在这个数据结构里,对头是某一个性质最极端的一个,这个性质是可以人为定义的,可以是一个字符串的字典序,也可以是数的大小等等;

本菜鸟就来介绍一下用数组实现堆的方法以及一些常规操作的实现;

因为堆是由一颗完全二叉树组成的,那么没一个点的父节点都在数组中定位了,也就是说有一个公式可以由一个点推出这个点的父节点和儿子节点;

记一个点的下标为 i,那么父节点的下标为 i/2

两个儿子节点的下标为 i/2 和 i/2+1;

有了这个基础,我们就开始实现堆的最重要的两个操作;

操作1:down(int i);

操作2:up(int i);

这两个函数就是将下标为 i 的点按照我们规定的规矩改变位置;

如果我们队里存的是int 类型的数据,而且我们规定父节点要大于子节点,那么我们就要用down和up函数来实现了;

先来看down函数的代码;

const int N=100010;

int heap[N],cont;//cont是用来存储堆里的元素个数的变量

void down(int i){
    int u=i;//记录初始位置
    
    if( u*2<=cont && heap[x]<heap[u*2])//这两个if是将heap[i]与两个子节点比较
        i=u*2; // 如果子节点大于父节点,那么为了维护子节点小于父节点的性质,那么我们就要把当前为父子节点的两个节点交换;

    if( u*2+1<=cont && heap[x]<heap[u*2+1])
        i=u*2+1;

    if(u!=i){ // u为初始时的i,如果检测到有子节点比父节点大,那么此刻i应该为子节点的下标
        swap(heap[u],heap[i]);//交换两个数

        down(i);//进入下一层;
    }
}

 其实up函数也差不多,只不过是将递归换成循环了

void up(int i){

    while(i/2>=1&&heap[i/2]<heap[i]){
         
         swap(heap[i],heap[i/2]);
         i/=2;
    }
}  

有了这两个函数,我们就可以实现删除第k个数、将第k个数变为x、添加一个数...等等操作了;

注意:堆里每一个元素的下标可以排列成一个公差为1的等差数列;

删除第k个数

void cut(int k){
     heap[k]=heap[cont];//heap[cont]为堆里最末尾的元素
   
     cont--;

     down(k);
}

这个属于一个技巧,那就将最末尾的元素覆盖掉第k个数,然后将元素个数cont-1,代表减少一个元素,然后再让第k个数down到最底层【heap[cont]原本就为最底层,down后还是最底层】

这个让我想起我小时候是怎么给我家狗铲屎的了,那就是先用沙子把shit覆盖,然后用铲子把shit给铲掉,最后shit就不见了;

然后是添加一个数

void add(int x){
     heap[++cont]=x;
    
     up(cont);
}

添加一个数,然后再让这个数up,之所以不用down是因为添加的时候该数就位于最底层,down不了;

最后是将第k个数变为x,其实和上面是一样的,换汤不换药罢了;

void change(int k,int x){
    heap[k]=x;

    up(k); down(k);
}

这个就要up和down都用一遍了,因为你不晓得你改后这个数是一个上漂还是下沉,但知道的是up和down函数起到改变位置作用的只有一个;

然后最重要的来了,那就是输入一些无序的数字,如何将这些数字变为我们规定下的堆呢?也就是建堆;

这个其实很简单,如果我们输入了n个数,那么我们只需要将1~n/2间的数字都down一遍,down完了之后就是严格遵循我们规定的堆了;

void get_heap(int heap[]){

    for(int i=1;i<=cont;i++){
         down(i);
    }

}

这里要千万注意,堆顶的下标一定不能为1,不能为0,因为0这个数字不满足我们的寻找父子节点的公式;

儿子节点:i * 2 or  i * 2 + 1;

父亲节点:i / 2

因为堆顶是所有堆里所有数据里极端的一个,所以可以用在迪杰斯特拉算法里,用来优化早最短点的步骤

AcWing 839. 模拟堆 - AcWing

AcWing 838. 堆排序 - AcWing

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值