算法系列05——堆和比较器

堆是一种特殊的完全二叉树,分为大根堆和小根堆。大根堆保证每个节点的值大于或等于其子节点,反之亦然。插入和删除操作的时间复杂度为O(logN)。堆排序算法通过构建和调整堆来实现排序,复杂度为O(NlogN)。优先级队列常基于堆实现,支持插入和删除操作,但不支持直接修改特定位置。比较器在排序和优先级队列中用于自定义元素的比较规则。
摘要由CSDN通过智能技术生成
    • buf + heapSize + limit(一般来说有,只用来判满)
    • 是一颗完全二叉树,要么是满二叉树,要么是从左往右依此变满
    • 完全二叉树的实现,数组从0出发的连续一段构成完全二叉树,左孩子2n+1,右孩子2n+2,父(n-1)/2,0算出来的父亲是自己
    • 堆分为大根堆和小根堆
      • 大根堆本身以及每颗子树最大值是头节点的值

    利用数组生成大根堆,从heapsize=0开始,每个数新进来时,放在下标heapsize的地方

    • heap insert(arr, i):要插入的数已经放置在数组的i=heapsize处了
      • 和自己的父节点进行比较,如果大了就要交换,不断和父比较,不断往上窜
      • 从而能保证每次加入一个数,保持一个大根堆
      • 可以只写一个while大于自己的父亲,但实际上表示了两个条件
    • 小根堆的扩容
      • 成倍扩容,每次扩容需要进行复杂度O(N)的复制
      • 扩容的次数时logN水平的
      • 加上扩容的代价,分摊到每一个树上O(NlogN)/N = O(logN),加入一个数的复杂度仍然是logN水平

    从大根堆中取出一个最大的数

    把最后一个数换到第一个位置,heapsize-1,最后一个位置已经不计入堆了

    • heapify(arr,index,heapsize)
      • 与左孩子和右孩子比较,将较大的孩子交换上来,交换下去的继续向下比较,不再小于时停止
      • 需要一个heapsize变量,来确定是否还有孩子
    • 当大根堆中间某个位置的值发生了改变
      • 如果变小了,从这个位置开始往下进行一个heapify
      • 如果变大了,从这个位置开始网上进行一个heapinsert
    • 堆的深度是logN
      • 因为有这么多层,用户进行新增一个数或者取出一个数的复杂度都是logN
    • 优先级队列
      • 不是队列而是堆,使用的时候像队列而已
      • 不支持已经形成堆结构的数组里面,去修改特定位置的数
      • 如果只需要加入弹出,使用现成的就好,要修改特定位置,使用较小的代价就调整好,需要自己实现
  • 堆排序:O(NlogN),O(1)

    • 输入是一个数组

    • 先让整个数组构成一个大根堆,从heapsize=0开始,不断向后看,相当于不断插入新的数,不断heapinsert

      • 当一次性拥有了所有数字时,其实有复杂度更低的做法,对数组逆序,每个位置作heapify
      • 这样子,所有的叶节点实际上都不用操作,只有O(1)复杂度看一眼,而叶节点有n/2个
      • 倒数第二层n/4个节点,O(2)复杂度,依此递推
      • T(N) = N/2 + N/4 2 + N/8 3 + ……
      • 构造2T(n),错项相减,生成等比数列,最后得到复杂度O(N)
    • 然后将第一个位置和最后一个位置的数做交换,heapsize–,相当于取出了最大的数,放在了数组最后面(堆外面)

    • 进行heapify,调整完之后,重复上一步骤,while(heapsize>0)

    • 额外空间复杂度O(1),没有任何的递归过程,时间复杂度O(NlogN)

    • 堆排序的应用:几乎有序的数组进行排序(移动最多k位)O(Nlogk)复杂度

      • 准备一个小根堆(直接用优先队列就好了),遍历数组前k+1个数,
      • 弹出小根堆的最小数,一定在0位置上,
      • 接着放入一个数,弹出一个数
      • 最后数组中的数全部放完,将小根堆中的数依此弹出即可
  • 比较器的使用

    • 实质上时重载比较运算符,配合stl,java现成函数使用

    • 一般现成排序默认从小到大

    • 比较器的返回值含义

      • 返回负数,第一个参数排在前面
      • 返回正数,第二个参数排在前面
      • 返回0,谁排前面无所谓
    • 如果数字

      • 降序返回o1-o2即可
      • 升序返回o2-o1即可
    • 比较器还可以用在有排序含义中的结构中

      • 如优先队列,默认时小根堆,定义比较器返回第二个数减第一个数,就生成大根堆
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值