【java多线程】队列系统之PriorityBlockingQueue源码
一、二叉堆
如题,二叉堆是一种基础数据结构
事实上支持的操作也是挺有限的(相对于其他数据结构而言),也就插入,查询,删除这一类
对了这篇文章中讲到的堆都是二叉堆,而不是斜堆,左偏树,斐波那契堆什么的 我都不会啊
二叉堆的表现形式:我们可以使用数组的索引来表示元素在二叉堆中的位置。
从二叉堆中,我们可以得出:
· 元素k的父节点所在的位置为 :k的位置/2]
· 元素k的子节点所在的位置为:2*k的位置和2*k的位置+1
跟据以上规则,我们可以使用二维数组的索引来表示二叉堆。通过二叉堆,我们可以实现插入和删除最大值都达到O(nlogn)的时间复杂度。
对于堆来说,最大元素已经位于根节点,那么删除操作就是移除并返回根节点元素,这时候二叉堆就需要重新排列;当插入新的元素的时候,也需要重新排列二叉堆以满足二叉堆的定义。现在就来看这两种操作。
实现优先级队列的思路及算法复杂度的考量
- 如果使用无序数组,那么每一次插入的时候,直接在数组末尾插入即可,时间复杂度为O(1),但是如果要获取最大值,或者最小值返回的话,则需要进行查找,这时时间复杂度为O(n)。
- 如果使用有序数组,那么每一次插入的时候,通过插入排序将元素放到正确的位置,时间复杂度为O(n),但是如果要获取最大值的话,由于元阿苏已经有序,直接返回数组末尾的 元素即可,所以时间复杂度为O(1).所以采用普通的数组或者链表实现,无法使得插入和排序都达到比较好的时间复杂度。所以我们需要采用新的数据结构来实现。下面就开始介绍如何采用二叉堆(binary heap)来实现优先级队列
- 跟据以上规则,我们可以使用二维数组的索引来表示二叉堆。通过二叉堆,我们可以实现插入和删除最大值都达到O(nlogn)的时间复杂度。
二、二叉堆
由于堆是一棵形态规则的二叉树,因此堆的父节点和孩子节点存在如下关系:
设父节点的编号为
i
, 则其左孩子节点的编号为2*i+1
, 右孩子节点的编号为2*i+2
设孩子节点的编号为i
, 则其父节点的编号为(i-1)/2
什么是二叉堆?
二叉堆本质上是一种完全二叉树,它分为两个类型:
1.最大堆
2.最小堆
什么是最大堆呢?最大堆任何一个父节点的值,都大于等于它左右孩子节点的值。
什么是最小堆呢?最小堆任何一个父节点的值,都小于等于它左右孩子节点的值。
二叉堆的根节点叫做堆顶。
最大堆和最小堆的特点,决定了在最大堆的堆顶是整个堆中的最大元素;最小堆的堆顶是整个堆中的最小元素。
堆的自我调整
对于二叉堆,如下有几种操作:
插入节点
删除节点
构建二叉堆
这几种操作都是基于堆的自我调整。
下面让我们以最小堆为例,看一看二叉堆是如何进行自我调整的。
1.插入节点
二叉堆的节点插入,插入位置是完全二叉树的最后一个位置。比如我们插入一个新节点,值是 0。
这时候,我们让节点0的它的父节点5做比较,如果0小于5,则让新节点“上浮”,和父节点交换位置。
继续用节点0和父节点3做比较,如果0小于3,则让新节点继续“上浮”。
继续比较,最终让新节点0上浮到了堆顶位置。
2.删除节点
二叉堆的节点删除过程和插入过程正好相反,所删除的是处于堆顶的节点。比如我们删除最小堆的堆顶节点1。
这时候,为了维持完全二叉树的结构,我们把堆的最后一个节点10补到原本堆顶的位置。
接下来我们让移动到堆顶的节点10和它的左右孩子进行比较,如果左右孩子中最小的一个(显然是节点2)比节点10小,那么让节点10“下沉”。
这样一来,二叉堆重新得到了调整。
3.构建二叉堆
构建二叉堆,也就是把一个无序的完全二叉树调整为二叉堆,本质上就是让所有非叶子节点依次下沉。
我们举一个无序完全二叉树的例子:
首先,我们从最后一个非叶子节点开始,也就是从节点10开始。如果节点10大于它左右孩子中最小的一个,则节点10下沉。
接下来轮到节点3,如果节点3大于它左右孩子中最小的一个,则节点3下沉。
接下来轮到节点1,如果节点1大于它左右孩子中最小的一个,则节点1下沉。事实上节点1小于它的左右孩子,所以不用改变。
接下来轮到节点7,如果节点7大于它左右孩子中最小的一个,则节点7下沉。
节点7继续比较,继续下沉。
这样一来,一颗无序的完全二叉树就构建成了一个最小堆。
堆的代码实现
在撸代码之前,我们还需要明确一点:
二叉堆虽然是一颗完全二叉树,但它的存储方式并不是链式存储,而是顺序存储。换句话说,二叉堆的所有节点都存储在数组当中。
数组中,在没有左右指针的情况下,如何定位到一个父节点的左孩子和右孩子呢?
像图中那样,我们可以依靠数组下标来计算
假设父节点的下标是parent,那么它的左孩子下标就是 2*parent+1;它的右孩子下标就是 2*parent+2 。
比如上面例子中,节点6包含9和10两个孩子,节点6在数组中的下标是3,节点9在数组中的下标是7,节点10在数组中的下标是8。
7 = 3*2+1
8 = 3*2+2
刚好符合规律。
有了这个前提,下面的代码就更好理解了:
参考:
https://www.cnblogs.com/yangecnu/p/Introduce-Priority-Queue-And-Heap-Sort.html
https://www.cnblogs.com/henry-1202/p/9307927.html
http://www.ijiandao.com/2b/baijia/168869.html
http://blog.jobbole.com/113552/
【java多线程】队列系统之PriorityBlockingQueue源码的更多相关文章
- Java并发包源码学习系列:阻塞队列实现之PriorityBlockingQueue源码解析
目录 PriorityBlockingQueue概述 类图结构及重要字段 什么是二叉堆 堆的基本操作 向上调整void up(int u) 向下调整void down(int u) 构造器 扩容方法t ...
- 【java多线程】队列系统之LinkedBlockingQueue源码
转载:https://blog.csdn.net/tonywu1992/article/details/83419448 http://benjaminwhx.com/archives/ 1.简介 上 ...
- 【java多线程】队列系统之LinkedBlockingDeque源码
1.简介 上一篇我们介绍了 LinkedBlockingDeque 的兄弟篇 LinkedBlockingQueue .听名字也知道一个实现了 Queue 接口,一个实现了 Deque 接口,由于 D ...
- 【java多线程】队列系统之ArrayBlockingQueue源码
1.简介 ArrayBlockingQueue,顾名思义:基于数组的阻塞队列.数组是要指定长度的,所以使用ArrayBlockingQueue时必须指定长度,也就是它是一个有界队列. 它实现了Bloc ...
- lesson2:java阻塞队列的demo及源码分析
本文向大家展示了java阻塞队列的使用场景.源码分析及特定场景下的使用方式.java的阻塞队列是jdk1.5之后在并发包中提供的一组队列,主要的使用场景是在需要使用生产者消费者模式时,用户不必再通过多 ...
- Java多线程学习之线程池源码详解
0.使用线程池的必要性 在生产环境中,如果为每个任务分配一个线程,会造成许多问题: 线程生命周期的开销非常高.线程的创建和销毁都要付出代价.比如,线程的创建需要时间,延迟处理请求.如果请求的到达率非常 ...
- Java并发编程笔记之PriorityBlockingQueue源码分析
JDK 中无界优先级队列PriorityBlockingQueue 内部使用堆算法保证每次出队都是优先级最高的元素,元素入队时候是如何建堆的,元素出队后如何调整堆的平衡的? PriorityBlock ...
- 【java多线程】队列系统之DelayQueue源码
一.延迟队列 延迟队列,底层依赖了优先级队列PriorityBlockingQueue 二.延迟队列案例 (1)延迟队列的任务 public class DelayTask implements De ...
- Java在线考试系统(含源码)
本文demo下载和视频教学观看地址:http://www.wisdomdd.cn/Wisdom/resource/articleDetail.htm?resourceId=1076 本实例介绍了在线考 ...
随机推荐
- java笔记 -- java数据类型与类型转换
Java是一种强类型语言, 这就意味着必须为每一个变量声明一种类型. Java中一共有8中基本类型: 4种整型: 整型: 用于表示没有小数部分的数值, 允许为负数 类型 存储需求 取值范围 int: ...
- leecode第一百四十八题(排序链表)
class Solution { public: void sort_list(ListNode* head1, ListNode* head2,int len)//在原链表上进行排序 { ListN ...
- Yahoo Programming Contest 2019 E - Odd Subrectangles
E - Odd Subrectangles 思路: 对于行方案固定的情况下,假设和为奇数的列为a个,和为偶数的列为b个,a+b = m 那么从奇数里面选奇数个,即C(a, 1) + C(a, 3) + ...
- 实现mypwd&mybash&myod&读者写者
目录: 一.mypwd 二.mybash 三.myod 四.读者.写者 一.实现mypwd 学习pwd命令 研究pwd实现需要的系统调用(man -k; grep),写出伪代码 实现mypwd 测试m ...
- 盘符格式转换成NTFS格式
点击屏幕左下角“开始”,找到运行,在其中输入:cmd,确定,打开“命令提示符”; 在命令提示符下输入: convert X:/FS:NTFS(其中X是你要转换的盘符盘符). 执行后,重新启动电脑 这样 ...
- 使用Metasploit渗透攻击windows系统(二)
后渗透攻击: 介绍常用的一些命令: 查看进程:ps 查看当前进程号:getpid 查看系统信息:sysinfo 查看目标机是否为虚拟机:run post/windows/gather/checkvm ...
- Hadoop---集群的时间同步
集群的时间同步(使用插件使从机和主机时间一致) 集群保障时间一致 共有3个方法 1.手工的改 date –s “2016-01-05” 2.启动service NTP 3.基于实体机 7*24 不关机 ...
- Win10系列:C#应用控件进阶2
矩形 若要绘制矩形需要用到Rectangle元素,通过指定Rectangle元素的Width和Height属性值来确定矩形的尺寸.而设置RadiusX和RadiusY属性值能得到圆角的矩形,这两个属性 ...
- Eclipse中Server视图加载项目之后项目名后边有带括号的名字
用习惯了eclipse工具,因为某种原因需要修改项目名称.结果选择项目,按“F2”成功修改后,使用tomcat进行web发布时,选择“Add and Remove”,发现名字还是以前那个项目名称.即使 ...
- Linux下实现ssh免密认证
添加域名映射 配置ssh免密登陆 拷贝master服务器公钥至本机 验证master服务器ssh免密登录其余服务器 添加域名映射 打开hosts文件 Vim /etc/hosts 添加域名对象 配置s ...