B站左神算法课学习笔记(P5):详解桶排序以及排序内容大总结

快速排序的空间复杂度

最差情况,每次都取到最大/最小值,一共展开 n 层递归,需要的空间复杂度为 O\left ( N \right )

最好情况,每次都取到中点值,相当于完全二叉树展开,需要的空间复杂度为O\left ( logN \right )

注意:用迭代代替递归无法节省空间!因为节省不下来!

递归实现了对于中点位置的存储,每层递归结束后才会释放相应资源,因为有了中点位置,快排才能借助递归实现。但是,若使用迭代,也需要手动记录中点位置,并使用栈结构,告诉计算机返回什么位置,以及该位置左右各是什么数。(自己的理解还是不太清楚,见谅)

逻辑概念上,堆是一个完全二叉树结构。

完全二叉树

若某个树为满二叉树,或其为从上往下从左往右依次含有节点的树,则称之为完全二叉树。

任意数组从下标为 0 出发的任意一段都可以转化为完全二叉树。

例:如上左图,取数组前 7 位构成完全二叉树。

在上述数组中任取 i 位置,其左孩子下标为 2 * i + 1,其右孩子下标为 2 * i + 2,父节点为\frac{i-1}{2}.

堆的分类

大根堆:任意子树的最大值为头节点的值

小根堆:任意子树的最小值为头节点的值

堆的基本操作

添加堆

给出一个新的数字,将新的数字插入已有的大根堆 / 小根堆中。

方法:插入节点后,与其父节点 \frac{i-1}{2} 的比较大小。

例:

注意\frac{i-1}{2}需要向下取整,且 i = 0 时,运算结果为 0 .

代码中使用 heapInsert 函数实现:

while 循环反复检查是否符合大根堆的条件,若不符合,令子节点与父节点处的数值交换,并更新 index 为其父节点,再次进入循环判断。

出堆

在已有的大根堆 / 小根堆中,弹出堆顶元素,并做相应调整。

方法:大根堆中,先将末尾元素补至堆顶,令 heapSize - 1,然后判断是否还有左右孩子,若有,求其左右孩子的最大值并与之比较,若其最大,则停止;否则,交换两者位置并重复上述步骤。

代码中使用 heapify 函数实现:

修改堆

在堆中,修改某一位置的元素,要求仍然保持大根堆 / 小根堆。

方法:检查元素变大 / 变小,若变大,则调用 heapInsert 函数;若变小,则调用 heapify 函数。

时间复杂度

完全二叉树的高度

已知有 N 个节点,则完全二叉树的高度为 logN 。

相应地,堆操作中,无论是插入、弹出、调整,时间复杂度都是 O\left ( logN \right ) 级别。

堆排序

先建堆,然后进行堆排序操作。

堆排序中,每次将堆顶与堆末数字交换,然后在 heapSize-- 的空间上整理为有序堆,再重复上述操作。当 heapSize == 0 时,排序完成。

演示

代码实现

其中 heapInsert() 函数和 heapify() 函数如前文所示,前者负责建堆,后者负责整理堆为有序堆。

时间复杂度 O\left (N logN \right ) 。

优化:

heapInsert() 函数中,若一次性给出全部数组,则可使用该方法:

思想:从右往左从下往上做 heapify ,每次只需解决对应子树中的问题

堆排序扩展题目

注意:Java中,优先队列就是小根堆!

关于堆的实际使用,补充以下几点:

  1. 扩容问题:以数组存储堆结构时,每次耗尽都成倍扩容。故数组长度为2,4,6,8...,每次扩容需要O(N) ,扩容次数为O(logN),故整体扩容代价为\frac{O\left ( NlogN \right )}{N}=O\left ( logN \right ) !
  2. *黑盒理念*:编系统提供的堆结构是一个”黑盒“,只能高效执行输入一个(add)和输出一个(poll)数。缺点:在已有的堆结构上改变某个数,并令其重新调整为堆结构,此时调整代价很高!但是自己手写的堆结构可以实现修改后的高效调整。
    若需要实现高效调整,只能手写堆!

代码实现

建堆 -> 加入&弹出 -> 弹出剩余

比较器

例:

准备数组:

下图中,第一个参数为对应数组,第二个参数则是设置比较器。

比较器实现举例:

上图注释说明了比较器的返回规则:

  • 负数——第一个参数在前
  • 正数——第二个参数在前
  • 0——任意在前

上图中绿色部分其实等价于:

用上图更清晰,而用一句 return 则更简洁。

同理也可依照此对其他参数进行排序,如:

作用:减少代码量

桶排序和基数排序

特点:不是基于”比较“的排序,而是基于数据状况产生的排序!

实际使用需要根据数据状况进行定制。

桶排序

基数排序

补充:计数排序 —— 基于类型进行统计、计数后排序

代码实现

相关函数实现:

tips:仅对于非负值!

解释:使用 count 数组完成分片!(或者说使用计数排序的结果模拟”桶“的概念)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值