PriorityQueue详解
2016-05-24 15:48
441 查看
优先级队列是不同于先进先出队列的另一种队列。每次从队列中取出的是具有最高优先权的元素。
PriorityQueue是从JDK1.5开始提供的新的数据结构接口。
如果不提供Comparator的话,优先队列中元素默认按自然顺序排列,也就是数字默认是小的在队列头,字符串则按字典序排列。
如果想实现按照自己的意愿进行优先级排列的队列的话,需要实现Comparator接口。下面的方法,实现了根据某个变量,来进行优先级队列的建立。
可以看到,虽然PriorityQueue保持了队列顶部元素总是最小,但内部的其它元素的顺序却随着元素的减少始终处于变化之中。由于没有总结出有效的规律.我们可以看到增加元素的时候是这样处理的
grow增加数量
siftUp这个才是我们的调整各自的优先级的重点哦
我们是否在构造的时候传递了比较的函数,如果没有,采用默认的比较策略。
这里我们就看看没有使用自己的比较策略的那种情况吧!
private transient Object[] queue;
/**
* The comparator, or null if priority queue uses elements’
* natural ordering.
*/
private final Comparator
PriorityQueue内部成员数组queue其实是实现了一个二叉树的数据结构,这棵二叉树的根节点是queue[0],左子节点是queue[1],右子节点是queue[2],而queue[3]又是queue[1]的左子节点,依此类推,给定元素queue[i],该节点的父节点是queue[(i-1)/2]。因此parent变量就是对应于下标为k的节点的父节点。
弄清楚了这个用数组表示的二叉树,就可以理解上面的代码中while循环进行的工作是,当欲加入的元素小于其父节点时,就将两个节点的位置交换。这个算法保证了如果只执行add操作,那么queue这个二叉树是有序的:该二叉树中的任意一个节点都小于以该节点为根节点的子数中的任意其它节点。这也就保证了queue[0],即队顶元素总是所有元素中最小的。
需要注意的是,这种算法无法保证不同子树上的两个节点之间的大小关系。举例来说,queue[3]一定会小于queue[7],但是未必会小于queue[9],因为queue[9]不在以queue[3]为根节点的子树上。
好像是非常的有趣,维护这个就是一个堆嘛!是不是,堆就是这样实现的涩!
我们在来看看删除的操作~
这个就是下乘的操作~~
这个函数的实现方法是,将队尾元素取出,插入到位置i,替代被删除的元素,然后做相应的调整,保证二叉树的有序,即任意节点都是以它为根节点的子树中的最小节点。进一步的代码就留给有兴趣的读者自行分析,要说明的是,对于queue这样的二叉树结构有一个特性,即如果数组的长度为length,那么所有下标大于length/2的节点都是叶子节点,其它的节点都有子节点。
这里是为啥呢?应为最后一个元素放在I的位置,破坏了之后的堆得有序性。我们不断的比较i 和 i的左孩子 右孩子 看看是否满足堆得意思。然后进调整!
这里使用的时候是先比较我们的左右孩子然后在进行和父节点比较~~如果有序的后面的就退出啦~这个就是堆排序中下乘的操作!!!懂了????
补充:
堆排序只能保证根是最大(最小),不能保证整体是按照顺序来排序的。
注意1:该队列是用数组实现,但是数组大小可以动态增加,容量无限。
注意2:此实现不是同步的。不是线程安全的。如果多个线程中的任意线程从结构上修改了列表, 则这些线程不应同时访问 PriorityQueue 实例,这时请使用线程安全的PriorityBlockingQueue 类。
方法iterator()中提供的迭代器并不保证以有序的方式遍历优先级队列中的元素。
至于原因可参考下面关于PriorityQueue的内部实现
如果需要按顺序遍历,请考虑使用 Arrays.sort(pq.toArray())。
PriorityQueue是从JDK1.5开始提供的新的数据结构接口。
如果不提供Comparator的话,优先队列中元素默认按自然顺序排列,也就是数字默认是小的在队列头,字符串则按字典序排列。
如果想实现按照自己的意愿进行优先级排列的队列的话,需要实现Comparator接口。下面的方法,实现了根据某个变量,来进行优先级队列的建立。
import java.util.Comparator; import java.util.PriorityQueue; import java.util.Queue; public class test { private String name; private int population; public test(String name, int population) { this.name = name; this.population = population; } public String getName() { return this.name; } public int getPopulation() { return this.population; } public String toString() { return getName() + " - " + getPopulation(); } public static void main(String args[]) { Comparator<test> OrderIsdn = new Comparator<test>(){ public int compare(test o1, test o2) { // TODO Auto-generated method stub int numbera = o1.getPopulation(); int numberb = o2.getPopulation(); if(numberb > numbera) { return 1; } else if(numberb<numbera) { return -1; } else { return 0; } } }; Queue<test> priorityQueue = new PriorityQueue<test>(11,OrderIsdn); test t1 = new test("t1",1); test t3 = new test("t3",3); test t2 = new test("t2",2); test t4 = new test("t4",0); priorityQueue.add(t1); priorityQueue.add(t3); priorityQueue.add(t2); priorityQueue.add(t4); System.out.println(priorityQueue.poll().toString()); } }
可以看到,虽然PriorityQueue保持了队列顶部元素总是最小,但内部的其它元素的顺序却随着元素的减少始终处于变化之中。由于没有总结出有效的规律.我们可以看到增加元素的时候是这样处理的
public boolean add(E e) { return offer(e); }
public boolean offer(E e) { if (e == null) throw new NullPointerException(); modCount++; int i = size; if (i >= queue.length) grow(i + 1); size = i + 1; if (i == 0) queue[0] = e; else siftUp(i, e); return true; }
grow增加数量
private void grow(int minCapacity) { int oldCapacity = queue.length; // Double size if small; else grow by 50% int newCapacity = oldCapacity + ((oldCapacity < 64) ? (oldCapacity + 2) : (oldCapacity >> 1)); // overflow-conscious code if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); queue = Arrays.copyOf(queue, newCapacity); } private static int hugeCapacity(int minCapacity) { if (minCapacity < 0) // overflow throw new OutOfMemoryError(); return (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE; }
siftUp这个才是我们的调整各自的优先级的重点哦
我们是否在构造的时候传递了比较的函数,如果没有,采用默认的比较策略。
private void siftUp(int k, E x) { if (comparator != null) siftUpUsingComparator(k, x); else siftUpComparable(k, x); }
这里我们就看看没有使用自己的比较策略的那种情况吧!
private transient Object[] queue;
/**
* The comparator, or null if priority queue uses elements’
* natural ordering.
*/
private final Comparator
private void siftUpComparable(int k, E x) { Comparable<? super E> key = (Comparable<? super E>) x; while (k > 0) { int parent = (k - 1) >>> 1; //>>> 这个是无符号的算术位移 Object e = queue[parent]; if (key.compareTo((E) e) >= 0) break; queue[k] = e; k = parent; } queue[k] = key; }
PriorityQueue内部成员数组queue其实是实现了一个二叉树的数据结构,这棵二叉树的根节点是queue[0],左子节点是queue[1],右子节点是queue[2],而queue[3]又是queue[1]的左子节点,依此类推,给定元素queue[i],该节点的父节点是queue[(i-1)/2]。因此parent变量就是对应于下标为k的节点的父节点。
弄清楚了这个用数组表示的二叉树,就可以理解上面的代码中while循环进行的工作是,当欲加入的元素小于其父节点时,就将两个节点的位置交换。这个算法保证了如果只执行add操作,那么queue这个二叉树是有序的:该二叉树中的任意一个节点都小于以该节点为根节点的子数中的任意其它节点。这也就保证了queue[0],即队顶元素总是所有元素中最小的。
需要注意的是,这种算法无法保证不同子树上的两个节点之间的大小关系。举例来说,queue[3]一定会小于queue[7],但是未必会小于queue[9],因为queue[9]不在以queue[3]为根节点的子树上。
好像是非常的有趣,维护这个就是一个堆嘛!是不是,堆就是这样实现的涩!
我们在来看看删除的操作~
public boolean remove(Object o) { int i = indexOf(o); if (i == -1) return false; else { removeAt(i); return true; } }
这个就是下乘的操作~~
private E removeAt(int i) { assert i >= 0 && i < size; modCount++; int s = --size; if (s == i) // removed last element queue[i] = null; else { E moved = (E) queue[s]; queue[s] = null; siftDown(i, moved); if (queue[i] == moved) { siftUp(i, moved); if (queue[i] != moved) return moved; } } return null; }
这个函数的实现方法是,将队尾元素取出,插入到位置i,替代被删除的元素,然后做相应的调整,保证二叉树的有序,即任意节点都是以它为根节点的子树中的最小节点。进一步的代码就留给有兴趣的读者自行分析,要说明的是,对于queue这样的二叉树结构有一个特性,即如果数组的长度为length,那么所有下标大于length/2的节点都是叶子节点,其它的节点都有子节点。
private void siftDown(int k, E x) { if (comparator != null) siftDownUsingComparator(k, x); else siftDownComparable(k, x); } private void siftDownComparable(int k, E x) { Comparable<? super E> key = (Comparable<? super E>)x; int half = size >>> 1; // loop while a non-leaf while (k < half) { int child = (k << 1) + 1; // assume left child is least Object c = queue[child]; int right = child + 1; if (right < size && ((Comparable<? super E>) c).compareTo((E) queue[right]) > 0) c = queue[child = right]; if (key.compareTo((E) c) <= 0) break; queue[k] = c; k = child; } queue[k] = key; }
这里是为啥呢?应为最后一个元素放在I的位置,破坏了之后的堆得有序性。我们不断的比较i 和 i的左孩子 右孩子 看看是否满足堆得意思。然后进调整!
这里使用的时候是先比较我们的左右孩子然后在进行和父节点比较~~如果有序的后面的就退出啦~这个就是堆排序中下乘的操作!!!懂了????
补充:
堆排序只能保证根是最大(最小),不能保证整体是按照顺序来排序的。
注意1:该队列是用数组实现,但是数组大小可以动态增加,容量无限。
注意2:此实现不是同步的。不是线程安全的。如果多个线程中的任意线程从结构上修改了列表, 则这些线程不应同时访问 PriorityQueue 实例,这时请使用线程安全的PriorityBlockingQueue 类。
方法iterator()中提供的迭代器并不保证以有序的方式遍历优先级队列中的元素。
至于原因可参考下面关于PriorityQueue的内部实现
如果需要按顺序遍历,请考虑使用 Arrays.sort(pq.toArray())。
相关文章推荐
- juey点击tr选中里面的radio
- STL之queue
- easyui1.32 各种问题汇总
- 模拟实现英汉字典(使用key/value形式的哈希表)
- 国产数据连接池Druid
- 虚幻4 改变默认UI Disable的Shader
- Django中嵌入ueditor并改进SyntaxHighlighter
- LinkIssue: Error 'LINK : fatal error LNK1123: failure during conversion to COFF: file invalid or cor
- 1017. Queueing at Bank (25)
- finished with non-zero exit value 2
- STL之deque
- KVC中setValuesForKeysWithDictionary:
- request threaded-only IRQs with IRQF_ONESHOT【转】
- Android高级UI GridView
- iOS-OC-UITableViewCell分割线自定义
- aidl.exe'' finished with non-zero exit value 1问题解决【转载】
- UICollectionViewLayout
- aidl.exe'' finished with non-zero exit value 1问题解决
- iOS学习之——UIView的setNeedsDisplay和setNeedsLayout方法
- easyui页面一打开的时候出现短暂混乱的问题,解决办法