返回顶部
首页 > 资讯 > 精选 >Java集合中堆的打开方式是什么
  • 730
分享到

Java集合中堆的打开方式是什么

2023-06-16 10:06:42 730人浏览 独家记忆
摘要

本篇内容主要讲解“Java集合中堆的打开方式是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java集合中堆的打开方式是什么”吧!什么是堆?堆其实就是一种特殊的队列—&a

本篇内容主要讲解“Java集合中堆的打开方式是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java集合中堆的打开方式是什么”吧!

什么是堆?

堆其实就是一种特殊的队列——优先队列。

普通的队列游戏规则很简单:就是先进先出;但这种优先队列搞特殊,不是按照进队列的时间顺序,而是按照每个元素的优先级来比拼,优先级高的在堆顶。

这也很容易理解吧,比如各种软件都有会员制度,某软件用了会员就能加速下载的,不同等级的会员速度还不一样,那就是优先级不同呀。

还有其实每个人回复微信消息也是默默的把消息放进堆里排个序:先回男朋友女朋友的,然后再回其他人的。

这里要区别于操作系统里的那个“堆”,这两个虽然都叫堆,但是没有半毛钱关系,都是借用了 Heap 这个英文单词而已。

我们再来回顾一下「堆」在整个 Java 集合框架中的位置:

Java集合中堆的打开方式是什么

也就是说,

  • PriorityQueue 是一个类 (class);

  • PriorityQueue 继承自 Queue 这个接口 (Interface);

那 heap 在哪呢?

heap 其实是一个抽象的数据结构,或者说是逻辑上的数据结构,并不是一个物理上真实存在的数据结构。

heap 其实有很多种实现方式,比如 binomial heap, Fibonacci heap 等等。但是面试最常考的,也是最经典的,就是 binary  heap 二叉堆,也就是用一棵完全二叉树来实现的。

那完全二叉树是怎么实现的?

其实是用数组来实现的!

所以 binary heap/PriorityQueue 实际上是用数组来实现的。

这个数组的排列方式有点特别,因为它总会维护你定义的(或者默认的)优先级最高的元素在数组的首位,所以不是随便一个数组都叫「堆」,实际上,它在你心里,应该是一棵「完全二叉树」。

这棵完全二叉树,只存在你心里和各大书本上;实际在在内存里,哪有什么树?就是数组罢了。

那为什么完全二叉树可以用数组来实现?是不是所有的树都能用数组来实现?

这个就涉及完全二叉树的性质了,我们下一篇会细讲,简单来说,因为完全二叉树的定义要求了它在层序遍历的时候没有气泡,也就是连续存储的,所以可以用数组来存放;第二个问题当然是否。

堆的特点

堆是一棵完全二叉树;

堆序性 (heap order): 任意节点都优于它的所有孩子。

a. 如果是任意节点都大于它的所有孩子,这样的堆叫大顶堆,Max Heap;

b. 如果是任意节点都小于它的所有孩子,这样的堆叫小顶堆,Min Heap

Java集合中堆的打开方式是什么

左图是小顶堆,可以看出对于每个节点来说,都是小于它的所有孩子的,注意是所有孩子,包括孙子,曾孙...

既然堆是用数组来实现的,那么我们可以找到每个节点和它的父母/孩子之间的关系,从而可以直接访问到它们。

Java集合中堆的打开方式是什么

比如对于节点 3 来说,

  • 它的 Index = 1,

  • 它的 parent index = 0,

  • 左孩子 left child index = 3,

  • 右孩子 right child index = 4.

可以归纳出如下规律:

  • 设当前节点的 index = x,

  • 那么 parent index = (x-1)/2,

  • 左孩子 left child index = 2*x + 1,

  • 右孩子 right child index = 2*x + 2.

有些书上可能写法稍有不同,是因为它们的数组是从 1 开始的,而我这里数组的下标是从 0 开始的,都是可以的。

这样就可以从任意一个点,一步找到它的孙子、曾孙子,真的太方便了,在后文讲具体操作时大家可以更深刻的体会到。

基本操作

任何一个数据结构,无非就是增删改查四大类:

功能方法时间复杂度
offer(E e)O(logn)
poll()O(logn)
无直接的 api删 + 增
peek()O(1)

这里 peek() 的时间复杂度很好理解,因为堆的用途就是能够快速的拿到一组数据里的最大/最小值,所以这一步的时间复杂度一定是 O(1)  的,这就是堆的意义所在。

那么我们具体来看 offer(E e) 和 poll() 的过程。

offer(E e)

比如我们新加一个 0 到刚才这个最小堆里面:

Java集合中堆的打开方式是什么

那很明显,0 是要放在最上面的,可是,直接放上去就不是一棵完全二叉树了啊。。

所以说,

  • 我们先保证加了元素之后这棵树还是一棵完全二叉树,

  • 然后再通过 swap 的方式进行微调,来满足堆序性。

这样就保证满足了堆的两个特点,也就是保证了加入新元素之后它还是个堆。

那具体怎么做呢:

Step 1.

先把 0 放在最后接上,别一上来就想着上位;

Java集合中堆的打开方式是什么

OK!总算先上岸了,然后我们再一步步往上走。

这里「能否往上走」的标准在于:

是否满足堆序性。

也就是说,现在 5 和 0 之间不满足堆序性,那么交换位置,换到直到满足堆序性为止。

这里对于最小堆来说的堆序性,就是小的数要在上面。

Step 2. 与 5 交换

Java集合中堆的打开方式是什么

此时 0 和 3 不满足堆序性了,那么再交换。

Step 3. 与 3 交换

Java集合中堆的打开方式是什么

还不行,0 还比 1 小,所以继续换。

Step 4. 与 1 交换

Java集合中堆的打开方式是什么

OK!这样就换好了,一个新的堆诞生了~

总结一下这个方法:

先把新元素加入数组的末尾,再通过不断比较与 parent 的值的大小,决定是否交换,直到满足堆序性为止。

这个过程就是 siftUp(),源码如下:

Java集合中堆的打开方式是什么

时间复杂度

这里不难发现,其实我们只交换了一条支路上的元素,

Java集合中堆的打开方式是什么

也就是最多交换 O(height) 次。

那么对于完全二叉树来说,除了最后一层都是满的,O(height) = O(logn)。

所以 offer(E e) 的时间复杂度就是 O(logn) 啦。

poll()

poll() 就是把最顶端的元素拿走。

对了,没有办法拿走中间的元素,毕竟要 VIP 先出去,小弟才能出去。

那么最顶端元素拿走后,这个位置就空了:

Java集合中堆的打开方式是什么

我们还是先来满足堆序性,因为比较容易满足嘛,直接从最后面拿一个来补上就好了,先放个傀儡上来。

Step1. 末尾元素上位

Java集合中堆的打开方式是什么

这样一来,堆序性又不满足了,开始交换元素。

那 8 比 7 和 3 都大,应该和谁交换呢?

假设与 7 交换,那么 7 还是比 3 大,还得 7 和 3 换,麻烦。

所以是与左右孩子中较小的那个交换。

Step 2. 与 3 交换

Java集合中堆的打开方式是什么

下去之后,还比 5 和 4 大,那再和 4 换一下。

Step 3. 与 4 交换

Java集合中堆的打开方式是什么

OK!这样这棵树总算是稳定了。

总结一下这个方法:

先把数组的末位元素加到顶端,再通过不断比较与左右孩子的值的大小,决定是否交换,直到满足堆序性为止。

这个过程就是 siftDown(),源码如下:

Java集合中堆的打开方式是什么

时间复杂度

同样道理,也只交换了一条支路上的元素,也就是最多交换 O(height) 次。

所以 offer(E e) 的时间复杂度就是 O(logn) 啦。

heapify()

还有一个大名鼎鼎的非常重要的操作,就是 heapify() 了,它是一个很神奇的操作,

可以用 O(n) 的时间把一个乱序的数组变成一个 heap。

但是呢,heapify() 并不是一个 public API,看:

Java集合中堆的打开方式是什么

所以我们没有办法直接使用。

唯一使用 heapify() 的方式呢,就是使用PriorityQueue(Collection c)

这个 constructor 的时候,人家会自动调用 heapify() 这个操作。

那具体是怎么做的呢?

哈哈源码已经暴露了:

从最后一个非叶子节点开始,从后往前做 siftDown().

因为叶子节点没必要操作嘛,已经到了最下面了,还能和谁 swap?

举个例子:

Java集合中堆的打开方式是什么

我们想把这个数组进行 heapify() 操作,想把它变成一个最小堆,拿到它的最小值。

那就要从 3 开始,对 3,7,5进行 siftDown().

Step 1.

Java集合中堆的打开方式是什么

尴尬 ?,3 并不用交换,因为以它为顶点的这棵小树已经满足了堆序性。

Step 2.

Java集合中堆的打开方式是什么

7 比它的两个孩子都要大,所以和较小的那个交换一下。

交换完成后;

Java集合中堆的打开方式是什么

Step 3.

最后一个要处理的就是 5 了,那这里 5 比它的两个孩子都要大,所以也和较小的那个交换一下。

Java集合中堆的打开方式是什么

换完之后结果如下,注意并没有满足堆序性,因为 4 还比 5 小呢。

Java集合中堆的打开方式是什么

所以接着和 4 换,结果如下:

Java集合中堆的打开方式是什么

这样整个 heapify() 的过程就完成了。

好了难点来了,为什么时间复杂度是 O(n) 的呢?

怎么计算这个时间复杂度呢?

其实我们在这个过程里做的操作无非就是交换交换。

那到底交换了多少次呢?

没错,交换了多少次,时间复杂度就是多少。

那我们可以看出来,其实同一层的节点最多交换的次数都是相同的。

那么这个总的交换次数 = 每层的节点数 * 每个节点最多交换的次数

Java集合中堆的打开方式是什么

这里设 k 为层数,那么这个例子里 k=3.

Java集合中堆的打开方式是什么

Java集合中堆的打开方式是什么

到此,相信大家对“Java集合中堆的打开方式是什么”有了更深的了解,不妨来实际操作一番吧!这里是编程网网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

--结束END--

本文标题: Java集合中堆的打开方式是什么

本文链接: https://lsjlt.com/news/283073.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Java集合中堆的打开方式是什么
    本篇内容主要讲解“Java集合中堆的打开方式是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java集合中堆的打开方式是什么”吧!什么是堆堆其实就是一种特殊的队列—&am...
    99+
    2023-06-16
  • Java中集合的迭代方式是什么
    本文小编为大家详细介绍“Java中集合的迭代方式是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“Java中集合的迭代方式是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。集合的迭代流使得程序员得以站在更高...
    99+
    2023-07-05
  • Java中List集合数据修改方式是什么
    这篇文章主要介绍“Java中List集合数据修改方式是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“Java中List集合数据修改方式是什么”文章能帮助大家解决问题。Java中List集合数据修...
    99+
    2023-07-05
  • Java中什么是Map集合
    小编给大家分享一下Java中什么是Map集合,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!一、什么是Map不同于List单列的线性结构,Java中的Map提供的是...
    99+
    2023-06-02
  • java的集合是什么?
    什么是集合?集合类存放于java.util包中。集合类型主要有3种:set(集)、list(列表)和map(映射)。集合存放的都是对象的引用,而非对象本身。所以我们称集合中的对象就是集合中对象的引用。简单来讲:集合就是一个放数据的容器,准确...
    99+
    2018-09-16
    java教程 java 集合
  • Java中Hashtable集合的常用方法是什么
    本篇内容介绍了“Java中Hashtable集合的常用方法是什么”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!public Object&n...
    99+
    2023-06-25
  • java中set集合遍历的方法是什么
    在Java中,Set集合可以通过迭代器(Iterator)或者增强for循环(foreach)来进行遍历。 使用迭代器遍历Set集合...
    99+
    2024-03-04
    java
  • java中集合的区别是什么?
    java中集合的区别是什么?在java中集合主要分为:List,Set,Map三种,其中List与Set是继承自Collection,而Map不是。List与Set的区别:List中的元素有存放顺序,并且可以存放重复元素,检索效率高,插入删...
    99+
    2014-12-16
    java教程 java 集合
  • java 中集合的原理是什么
    这期内容当中小编将会给大家带来有关java 中集合的原理是什么,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。一、概述集合是一种长度可变,存储数据的数据结构多样,存储对象多样的一种数据容器。Java中集合可...
    99+
    2023-06-20
  • Ptoshop5s中文件的打开方式是什么
    Ptoshop5s中文件的打开方式是什么,很多新手对此不是很清楚,为了帮助大家解决这个难题,下面小编将为大家详细讲解,有这方面需求的人可以来学习下,希望你能有所收获。打开一个文件,点击页面左上角文件----打开-----选择打开文件打开多个...
    99+
    2023-06-26
  • Java中的set集合是什么意思
    目录引言概念HashSet集合LinkedHashSet集合:TreeSet集合:实战场景引言 在前面的内容中,我们先是一一介绍了Collection集合中都有哪些种类的集合,并且详...
    99+
    2024-04-02
  • java集合流过滤的方法是什么
    Java集合流过滤的方法是使用filter()方法。filter()方法接受一个Predicate参数,用于筛选集合中满足条件的元素...
    99+
    2023-09-11
    java
  • java 中集合的实现原理是什么
    本篇文章给大家分享的是有关java 中集合的实现原理是什么,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。1、HashMappublic class Hash...
    99+
    2023-06-20
  • Java集合框架是什么
    这篇文章主要介绍了Java集合框架是什么,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。一、简介1、集合框架介绍Java集合框架提供了一套性能优良,使用方便的接口和类,他们位于...
    99+
    2023-06-29
  • java两个对象集合取差集的方法是什么
    在Java中,可以使用以下几种方式来取两个对象集合的差集:1. 使用循环遍历方式:遍历第一个集合,检查每个元素是否存在于第二个集合中...
    99+
    2023-08-25
    java
  • Python中的集合是什么
    这篇文章主要为大家展示了“Python中的集合是什么”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Python中的集合是什么”这篇文章吧。一、什么是集合二、集合的创建方式集合中的元素不能重复#地...
    99+
    2023-06-29
  • java中的ArrayList集合的初始化方式
    概述ArrayList是一个以动态数组为基础实现的非线程安全的集合,ArrayList的元素可以为空、可以重复,同时又是有序的(读取和存放的顺序一致 )。ArrayList继承AbstractList,实现了List、RandomAcces...
    99+
    2018-04-25
    java基础 java ArrayList 集合 初始化
  • Java中对象打包的正确方式是什么?
    Java是一门面向对象的编程语言,在Java中,对象是非常重要的概念。对象的创建和管理对于Java程序的运行效率和稳定性有着至关重要的影响。在Java中,对象的打包也是非常重要的一部分,本篇文章将会介绍Java中对象打包的正确方式。 什么是...
    99+
    2023-07-23
    打包 接口 对象
  • java Web报表集成的方式是什么
    本篇内容主要讲解“ java Web报表集成的方式是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“ java Web报表集成的方式是什么”吧!一般问这个问题的都是想咨询有没有和.net 平台...
    99+
    2023-06-03
  • java判断集合是否为空的方法是什么
    在Java中,判断集合是否为空有几种方法可以使用: 使用集合的isEmpty()方法:该方法返回一个boolean值,表示集合是否...
    99+
    2024-03-06
    java
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作