java 基础知识学习 priorityQueue
ArrayList:动态扩容(相对于数组),数组实现查询非常快但要求连续内存空间。
双向队列LinkedList:不需要像ArrayList一样创建连续的内存空间,它以链表的形式连接各个节点,但是查询搜索效率极低。
HashMap存放键值对:内部使用数组加链表实现,检索快但是由于键是按照Hash值存储的,所以无序,在某些情况下不合适。
TreeMap:使用优化了的排序二叉树(红黑树)作为逻辑实现,物理实现使用一个静态内部类Entry代表一个树节点,这是一个完全有序的结构,但是每个树节点都需要保存一个父节点引用, 左右孩子节点引用,还有一个value值,虽然效率高但开销很大。
今天我们将要介绍的PriorityQueue优先队列,更多的可以理解为是上述所有集合实现的一种折中的结构,它逻辑上使用堆结构(完全二叉树)实现,物理上使用动态数组实现,并非像TreeMap一样完全有序,但是如果按照指定方式出队,结果可以是有序的
(补充:完全二叉树:完全二叉树的定义:深度为k,有n个结点的二叉树当且仅当其每一个结点都与深度为k的满二叉树中编号从1至n的结点一一对应时,称为完全二叉树。)
一:堆结构的简单介绍:
堆结构在逻辑上是完全二叉树,物理存储上是数组。
但是完全二叉树并不是堆结构,堆结构是不完全有序的完全二叉树。我们知道完全二叉树有个非常大的优点,你可以从任意节点根据公式推算出该节点的左右孩子节点的位置以及父节点的位置(也是应为这个优点的原因,物理上用数组的结构来存,因为数组可以随机访问任意位置。)
当前节点位置为 i ,父节点位置 i/2,左孩子节点位置2*i,右孩子节点2*i+1。利用这个特性,我们就不必维护节点与节点之间的相互引用,TreeMap中定义一个Entry类,分别一个parent引用,left引用,right引用,并使用它们维护当前节点和别的节点之间的关系。而我们利用完全二叉树的这种特性,完全可以用数组作为物理存储。上述完全二叉树可以存储为以下的数组:
逻辑结构:
物理结构:
堆分大根堆(父节点比子节点的值大)和小根堆(父节点的值比子节点的值小),左右孩子节点的值的大小没有要求,所以我们说堆是不完全有序结构。
添加元素过程:
- 将新元素添加到堆结构的末尾(不论该值的大小)。
- 不断调整直至满足堆结构。
删除元素的过程:
- 用最后一个元素替换将要被删除的元素并删除最后元素。
- 判断该节点的值与其子节点中最小的值比较,如果小于最小值则维持堆结构,否则向下调整。
- 判断该节点的值是否小于父节点的值,如果小于则向上调整,否则维持堆结构。
上面是优先队列的基本原理,下面介绍具体的使用方法;
二: PriorityQueue实例
PriorityQueue中的元素在逻辑上构成了一棵完全二叉树,但是在实际存储时转换为了数组保存在内存中,而我们的PriorityQueue继承了接口Queue,表名这是一个队列,只是它不像普通队列(例如:LinkedList)在遍历输出的时候简单的按顺序从头到尾输出,PriorityQueue总是先输出根节点的值,然后调整树使之继续成为一棵完全二叉树 样每次输出的根节点总是整棵树优先级最高的,要么数值最小要么数值最大。下面我们看如何构造一个PriorityQueue实例。
//默认用于存储节点信息的数组的大小 private static final int DEFAULT_INITIAL_CAPACITY = 11; //用于存储节点信息的数组 transient Object[] queue; //数组中实际存放元素的个数 private int size = 0; //Comparator比较器 private final Comparator<? super E> comparator; //用于记录修改次数的变量 transient int modCount = 0;
主要有四个构造函数
public PriorityQueue() { this(DEFAULT_INITIAL_CAPACITY, null); } public PriorityQueue(int initialCapacity) { this(initialCapacity, null); } public PriorityQueue(Comparator<? super E> comparator) { this(DEFAULT_INITIAL_CAPACITY, comparator); } public PriorityQueue(int initialCapacity,Comparator<? super E> comparator) { if (initialCapacity < 1) throw new IllegalArgumentException(); this.queue = new Object[initialCapacity]; this.comparator = comparator; }
三 PriorityQueue 不是线程安全的。
四 add() 和 offer()区别
看源码, add(e) 内部offer(e)
五 poll() 和 peek() 区别?
poll() 取得队首元素,并且删除。
peek() 取得队首元素,但不删除;