数组的活学活用(一)

概览

数组我们知道,是一种有序连续的数据结构,随机访问的效率高。之所以随机访问的效率高,就是因为它的每个值都有下标,可以根据下标直接找到我们想要的值。

既然我们已经知道了这种思想,那么我们可以用来做什么呢?

接下来我们就讲讲用数组的思想,或者干脆就直接用数组实现的几种巧妙的思路。

堆排序

首先堆是什么,我理解就是一颗完全二叉树(当然,也有说多叉树也行,但是我没有研究过。);然后每个节点的值都大于等于它的子节点的值,则为最大堆,反之为最小堆。

接着我们就来看看最小堆是怎么样的一个东西。首先假设有一组数(3、9、2、5、1、6、10、16、7)。那我们初始化成最小堆时是什么个样子呢?

  1. 首先是按顺序取值,也按顺序存值
  2. 存入之后判断与父节点的大小
    1. 如果小于父节点,则交换两者位置
      1. 循环此步骤
    1. 否则继续处理下一个数。
  1. 如果单纯只想要最大最小值的话,到这步其实已经结束了。因为初始化的堆已经保证了根节点就是最大或者最小值
  2. 如果要继续使堆完全排序,可以将根节点与最后一个节点交换。此时最后一个节点为排序区
  3. 然后将根节点与子节点比较,若子节点比根节点小,则取最小的一个子节点与根节点交换。交换之后继续与子节点判断,直到无需交换。
  4. 如此重复4 5步骤,即可得到一个完全排序的堆

总结一下就是:1、初始堆。2、交换根和最后一个未排序的节点,重新调整堆,再交换。如此直到最后就能得到一个完全排序的堆了。

初始化堆

所以我们可以看出,最小堆(或者最大堆)都是父节点和子节点之间的大小关系判断,兄弟节点是没有大小顺序,此时就可以取min或者max值了,如果要完全排序,则可以继续按下面的步骤处理。

完全堆排序

看,经过几轮处理之后,我们就能得到一个完全经过排序的二叉树了。

与数组的关系

说了这么多,那么与数组到底有什么关系呢?其实很简单,这是因为堆的逻辑顺序是一颗二叉树,但是物理顺序却是一个数组。

怎么说呢?我们在存储的时候是将堆从上到下,从左到右平铺到数组中,通过下标来判断父子节点的关系。就以初始化堆之后的二叉树为例,在数组中的存储顺序是:

此时存储形式有2种:

  1. 下标0存根节点
    1. 假设当前节点的下标为n,则它的父节点为 (n-1)>>1。它的两个子节点分别为:(n<<1)+1和 (n<<1)+2
  1. 下标0不存值
    1. 假设当前节点的下标为n,则它的父节点为 n>>1。它的两个子节点分别为:n<<1和 (n<<1)+1

如此,我们也知道了,可以用数组来存一颗二叉树,而且可以很方便的找到它有关联的父节点甚至祖父节点等。

总结

堆排序,常用于取最大最小值,或者优先级队列和作业调度等。像Java中的PriorityQueue就用到了堆排序,然后JUC中的ScheduledThreadPoolExecutor中也是用到了堆排序。

定时任务调度池的基本实现原理是:

  • 将调度任务提取到线程池之前,首先计算下一次需要执行的时间戳,通过时间戳来判断优先级,并将任务存入最小堆中,这样就确保了最先要执行的任务一定在最小堆的顶部,也就是跟节点。
  • 然后开一个定时任务,拿堆中第一个任务和当前时间进行比较:
    • 如果下一次执行的时间小于等于当前时间,则将该任务从堆中移除,并且投入线程池中执行。
    • 如果执行时间大于当前时间,则不处理,因为队列中最先应该执行的任务都还没到时间,其他任务也一定没有到执行时间。

到这里我们的小知识就告一段落了,今天的代码比较少,感兴趣的小伙伴可以根据司机尝试自己实现一下。那我们下期再见了~

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值