文章首发于「陈树义」公众号及个人博客 shuyi.tech

其实这个问题的完整描述是:Java 中的 PriorityQueue 实现,其数据的逻辑结构是线性结构吗?其数据的物理结构又是什么?

估计很多人的答案是:PriorityQueue 是线性结构,因为 PriorityQueue 是优先级队列的实现,队列不就是线性结构的吗?但在PriorityQueue 的实现中,其数据的逻辑结构是树形结构,其物理结构是顺序存储结构。

要弄明白这个问题,我们必须先弄明白什么是数据的逻辑结构,什么是数据的物理结构。

顾名思义,数据的逻辑结构指的是数据是怎么组织起来的,数据的物理结构指的是数据是怎么存储的。 数据的逻辑结构与物理结构,是数据结构两个非常重要的要素。但你知道数据有几种逻辑结构、几种物理结构吗?

数据的逻辑结构

数据的逻辑结构指的是数据是如何组织起来的,反映数据元素之间的逻辑关系,它更加贴近于现实。 例如现实生活中树干与树叶就是树形结构,排队的队伍就是线性关系。

数据的逻辑结构一般有四种,分别是:线性结构、集合结构、树形结构、网络结构。 其中,我们把集合、树形结构、网络结构统称为非线性结构。

线性结构

线性结构指的是数据结构中的元素存在一对一的相互关系。 例如:数组是一种线性结构,其下标与元素一一对应。链表也是一种线性结构,其元素之间是一个接着一个的。生活中有很多类似的例子,例如排队买票的队伍就是一个线性结构。超市里排布整齐的商品,也是一个线性结构。

集合结构

集合结构指的是元素之间除了「同属一个集合」的关系之外,再无其他关系。 例如数学中的整数就是一个集合,所有小数也是一个集合。

树形结构

树形结构指的是元素存在一对多的关系。 例如水果有香蕉、草莓、西瓜等,这种逻辑结构就是树形结构。

网络结构

网络结构指的是元素存在多对多的关系。 网络结构也叫做网状结构,它是多对多的关系。比如城市的交通网络,每栋房子与其他房子都有许多条路。

数据的物理存储结构

数据的物理结构指的是数据是如何存储的,反映数据的存储结构。 数据的存储结构分为四种:顺序存储、链式存储、索引存储、散列存储。 一般我们将这四种物理结构分为顺序存储结构与非顺序存储结构。顺序存储是顺序存储结构,链式存储、索引存储、散列存储均属于非顺序存储结构。

数据的顺序存储结构的特点是:借助元素的相对位置来表示数据元素之间的逻辑关系。非顺序存储的特点是:借助指示元素存储地址的指针表示数据元素之间的逻辑关系。

注:有些朋友说数据的物理存储结构只有顺序存储、链式存储两种,索引存储和散列存储是不存在的。对此,后续我专门写篇文章来聊聊这个事情。

顺序存储

顺序存储指的是数据在内存当中是按照顺序存储的,逻辑结构上相邻的元素在物理结构上也是相邻的。Java 中的 ArrayList 就是通过顺序存储的方式实现的。

链式存储

链式存储指的是元素之间的逻辑顺序,并不是通过内存顺序来分配的,而是通过一个引用组织起来的。也就是说,逻辑上相邻的元素,在物理存储位置上是不相邻的。Java 中的 LinkedList 就是通过链式存储的方式实现的。

索引存储

索引存储指的是元素之间的逻辑关系,是通过一张索引表来存储的。这张索引表有很多个索引项,每个索引项存储两个信息:关键字、数据存储地址。我们通过关键字可以找到对应的数据存储地址。这就像书籍的目录一样,关键字就是章节名,数据存储地址就是页码。我们通过章节名可以快速地找到对应的页码,从而快速地找到书籍对应内容。

散列存储

散列存储也称之为哈希存储,其与索引存储非常类似,都是通过索引值以及对应的值来实现快速查找。唯一不同的区别是,索引存储会对索引值进行哈希。应该说散列存储是索引存储的一种更加复杂的实现。

辨别思路

看到这里,我们对数据的逻辑结构、物理结构已经有了基本的认识,也知道它们的常见种类。那我们到底如何去判断它们是属于哪种逻辑结构、哪种物理结构呢?

拿 Java 中对于优先级队列的 PriorityQueue 实现为例。通过阅读源码我们得知其底层使用了二叉堆实现,而二叉堆本身其实就是一颗二叉树。即对于 PriorityQueue 来说,其数组最终是通过下图这种逻辑结构组织起来的,因此 PriorityQueue 的逻辑结构是树形结构。

从 PriorityQueue 的源码,我们可以知道 PriorityQueue 的数据最终是通过一个对象数组存储的,而数组的物理结构是顺序存储的。因此对于 PriorityQueue 来说,其物理结构是顺序存储结构。

通过 PriorityQueue 这个例子,我们可以总结出判断数据的逻辑结构、物理结构的思路:判断逻辑结构,要看数据是如何组织起来的。要判断物理结构,则是要看数据最终是如何存储的。

如果对 PriorityQueue 的源码实现感兴趣,可以阅读:集合系列 Queue(九):PriorityQueue - 陈树义的博客

我们再用这个办法来判断一下 TreeMap 这个类。

TreeMap 是 Java 中非常经典的实现,其底层是基于红黑树的实现,即其最终会将所有数据组织成一颗红黑树,因此 TreeMap 的逻辑结构是树形结构。通过阅读 TreeMap 的源码,我们知道 TreeMap 的数据最终是通过 Entry 这个类存储的,因此 TreeMap 的物理结构是链式存储结构。

如果对 TreeMap 的源码实现感兴趣,可以阅读:集合系列 Map(十五):TreeMap - 陈树义的博客

最后我们拿比较复杂的 LinkedBlockingQueue 来分析一下。

很多人会说队列就是一种特殊的数组,而数组是顺序存储的,因此队列就是顺序存储的。如果将这个结论套用在 LinkedBlockingQueue 上,那是否可以得出同样的结论,即 LinkedBlockingQueue 也是顺序存储的呢?

前面我们说过:不要用直觉去判断,而要根据定义去判断。 即判断数据的逻辑结构、物理结构的思路是:判断逻辑结构,要看数据是如何组织起来的。要判断物理结构,则是要看数据最终是如何存储的。

LinkedBlockingQueue 是 Java 中的阻塞队列实现,其最终会将数组组织成一个队列,因此其逻辑结构上属于线性表。通过阅读源码,我们可以知道 LinkedBlockingQueue 的元素是存储在 Node 节点上的,因此 LinkedBlockingQueue 的物理结构属于链式存储。

总结

本文一开始通过 PriorityQueue 的例子,引入了逻辑结构与物理结构这个话题。接着介绍了四种逻辑结构:线性表、集合、树状结构、网络结构。

紧接着介绍了四种物理存储结构,分别是:顺序存储、链式存储、索引存储、哈希存储。

接着通过 PriorityQueue 的例子,得出判断数据的逻辑结构和物理结构的思路:判断逻辑结构,要看数据是如何组织起来的。要判断物理结构,则是要看数据最终是如何存储的。 最后再通过 TreeMap 和 LinkedBlockingQueue 这两个例子,带大家实践判断思路。

看到这里文章就结束了,如果你喜欢本篇文章,欢迎点赞、在看、转发、留言支持我,我们下次见~

参考资料

PriorityQueue 是线性结构吗?90%的人都搞错了!的更多相关文章

  1. Java引用类型原理深度剖析,看完文章,90%的人都收藏了

    本文为synchronized系列第二篇.主要内容为分析偏向锁的实现. 偏向锁的诞生背景和基本原理在上文中已经讲过了. 本文将分为几块内容: 1.偏向锁的入口 2.偏向锁的获取流程 3.偏向锁的撤销流 ...

  2. 90%的人都不知道的Node.js 依赖关系管理(下)

    转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 原文参考:https://dzone.com/articles/node-dependency-manage ...

  3. 90%的人都不知道的Node.js 依赖关系管理(上)

    转载请注明出处:葡萄城官网,葡萄城为开发者提供专业的开发工具.解决方案和服务,赋能开发者. 原文参考:https://dzone.com/articles/nodejs-dependency-mana ...

  4. 关于 junit4 90% 的人都不知道的特性,详解 junitperf 的实现原理

    前言 上一节介绍了 https://github.com/houbb/junitperf 的入门使用. 这一节我们从源码的角度,剖析一下其实现方式. 性能测试该怎么做? Junit Rules jun ...

  5. 通过有序线性结构构造AVL树

    通过有序线性结构构造AVL树 本博客旨在结局利用有序数组和有序链表构造平衡二叉树(下文使用AVL树代指)问题. 直接通过旋转来构造AVL树似乎是一个不错的选择,但是稍加分析就会发现,这样平白无故做了许 ...

  6. D_S 线性结构

    线性结构的定义:若结构是非空有限集,则有且仅有一个开始结点和一个终端结点,并且所有结点都最多只有一个直接前驱和一个直接后继. 线性结构的特点: 只有一个首结点和尾结点 除首尾结点外,其他结点只有一个直 ...

  7. java数据结构--线性结构

    一.数据结构 数据结构由数据和结构两部分组成,就是将数据按照一定的结构组合起来,这样不同的组合方式有不同的效率,可根据需求选择不同的结构应用在相应在场景.数据结构大致 分为两类:线性结构(如数组,链表 ...

  8. c++(线性结构的处理)

    我们知道,在内存中的空间都是连续的.也就是说,0x00000001下面的地址必然是0x00000002.所以,空间上是不会出现地址的突变的.那什么数据结构类型是连续内部空间呢,其实就是数组,当然也可以 ...

  9. 线性结构与树形结构相互转换(ES6实现)

    前言 当树形结构的层级越来越深时,操作某一节点会变得越来越费劲,维护成本不断增加.所以线性结构与树形的相互转换变得异常重要! 首先,我们约定树形结构如下: node = { id: number, / ...

随机推荐

  1. 2019牛客多校第九场B Quadratic equation(二次剩余定理)题解

    题意: 传送门 已知\(0 <= x <= y < p, p = 1e9 + 7\)且有 \((x+y) = b\mod p\) \((x\times y)=c\mod p\) 求解 ...

  2. Vue UI lib missing vue bug

    Vue UI lib missing vue bug Error Uncaught TypeError: Cannot read property 'prototype' of undefined a ...

  3. js sort map by key

    js sort map by key Map map to array // Array.from() Object let obj = {}; for(let key of Object.keys( ...

  4. React Suspense All In One

    React Suspense All In One 挂起让组件在渲染之前"等待"某些东西. 如今,Suspense仅支持一种用例:使用React.lazy动态加载组件. 将来,它将 ...

  5. PostgreSQL All In One

    PostgreSQL All In One SQL macOS https://www.postgresql.org/download/macosx/ EDB installer PostgreSQL ...

  6. Android低功耗蓝牙(蓝牙4.0)——BLE开发(上)

    段时间,公司项目用到了手机APP和蓝牙设备的通讯开发,这里也正好对低功耗蓝牙(蓝牙4.0及以后标准)的开发,做一个总结. 蓝牙技术联盟在2010年6月30号公布了蓝牙4.0标准,4.0标准在蓝牙3.0 ...

  7. git & Angular git commit 规范

    git & Angular git commit 规范 https://github.com/angular/angular/commits/master https://github.com ...

  8. git merge & git rebase

    git merge & git rebase bug error: You have not concluded your merge (MERGE_HEAD exists). hint: P ...

  9. Build your own React

    Build your own React https://pomb.us/build-your-own-react/ https://github.com/pomber/didact demo htt ...

  10. c++ readIntger writeIntger

    类似CE的read/writeIntger函数(外部) #include <iostream> #include <Windows.h> #include <TlHelp ...