1.什么是优先队列
优先队列就是具备优先级的队列,优先级可以用队列里面的值大小表示,也就是值越大表示的优先级越高,所以,优先队列就是可以删除最大元素和插入元素的队列。
2.什么是堆
堆又称为二叉堆,二叉堆是一组能够用堆有序的完全二叉树排序的元素,并在数组中按照层次存储(不使用数组的第一个位置)。
3.为什么要使用堆来实现优先队列
由于优先队列需要对其存储的数据进行大小排列(即数据的优先级大小),使用二叉堆对队列进行排序后,优先队列的插入元素和删除最大元素的最坏情况的运行时间都是logN级别的,运行时间很小,所以使用二叉堆实现优先队列。
为什么二叉堆对队列进行排序后,
插入元素和删除最大元素的最坏情况的运行时间都是logN级别?
由于优先队列是用二叉堆实现的,当插入元素时,其只需要比较是否比自己的根节点大,而不需要与根节点的同一级别的节点进行比较,也就相当于少比较了将近一半二叉树的数据,因此是logN级别的运行时间。当删除最大元素时,需要先将最大元素的值与最后一个节点的位置互换,然后再对互换后的二叉堆进行排序,原理与插入元素一样,都只需要比较一半的二叉堆数据。
4.怎样用二叉堆对队列元素进行排序(即插入元素和删除最大元素)
(1)插入元素
插入元素的代码实现用swim(n)方法,其实现如下:
- //判断插入的元素是否比它的根节点元素大
- //如果比根节点大,就与根节点互换位置,根节点的下标为n/2
- private void swim(int n){
- while(n>1&&less(n/2,n)){
- exch(n/2,n);
- n=n/2;
- }
- }
(2)删除最大元素
删除最大元素首先得将根节点与最后一个子节点的位置互换,然后再实现删除操作,最后再通过delMax()方法实现删除后的队列排序。delMax()方法的实现代码如下:
- private int delMax(){
- int max = pq[1];//pq[]数组用于存储优先队列的值,pq[1]就是最大值,不适用pq[0]
- exch(1,N--);//交换最大元素和最后一个节点
- pq[N+1]=null;//删除最大元素,防止对象游离
- sink(1);//对优先队列重新排序
- return max;//返回删除的最大元素
- }
上述代码使用的sink(k)方法的实现如下:
- private void sink(int k){
- while(k*2<=N){
- int j = k*2;
- if(less(j,j+1)) j++;
- if(!less(k,j)) break;
- exch(k,j);
- k=j;
- }
- }
5.用二叉堆实现的优先队列的全部代码如下:
- public class MaxPQ<Key extends Comparable<key>>{
- private Key[] pq;
- private int N = 0;
- pulbic MaxPQ(int maxN){
- //值存储在pq[1...N]中,pq[0]不适用,所以数组的长度为maxN+1
- pq = (Key[]) new Comparable[maxN+1];
- }
- public boolean isEmpty(){
- return N==0;
- }
- public int size(){
- return N;
- }
- public void insert(Key v){
- pq[++N] = v;
- swim(N);
- }
- public Key delMax(){
- Key max = pq[1];
- exch(q,N--);
- pq[N+1]=null;
- sink(1);
- return max;
- }
- //上浮
- private void swim(int n){
- while(n>1&&less(n/2,n)){
- exch(n/2,n);
- n=n/2;
- }
- }
- //下沉
- private void sink(int k){
- while(k*2<=N){
- int j = k*2;
- if(less(j,j+1)) j++;
- if(!less(k,j)) break;
- exch(k,j);
- k=j;
- }
- }
- private boolean less(int i,int j){
- //return pq[i]<pq[j]?true:false;
- return pq[i].compareTo(pq[j])<0;
- }
- private void exch(int i,int j){
- Comparable temp;
- temp = pq[i];
- pq[i] = pq[j];
- pq[j] = temp;
- }
- }
6.堆排序
(1)什么是堆排序
堆排序就是采用基于堆的优先队列对需要排序的数据进行排序,排序规则是按照从小到大排序的。
(2)堆排序的基本思想
由于基于堆的优先队列是按照从大到小进行排序的,所以可以采用基于堆的优先队列的delMax()方法的思想,在删
除元素之前将最大值与最后一个节点进行位置的互换,再将最大值删除。根据这个思想,可以每次将已排序好的优先队
列的最大值于最后一个节点互换,换完位置后这个优先队列的长度就缩减1,然后再对新的优先队列采用sink()方法进行
重新排序。以此类推,可以将整个优先队列按照从小到大排序。
(3)堆排序的代码实现
- private static void heapSort(Comparable[] a){
- //对任意顺序的数组a用基于堆的优先队列方法对其进行排序
- int n = a.length;
- for(int k=n/2;k>=1;k--){
- //一个根节点一个根节点的进行排序
- sink(a,k,n);
- }
- //对排序好的优先队列按照从小到大进行排序
- while(n>1){
- //先将最大值与最后一个节点位置进行互换
- exch(a,1,n--);
- //对新的队列进行排序
- sink(a,1,n);
- }
- }
- //对数组a所实现的优先队列,从第k个节点到第n个节点进行排序
- private static void sink(Comparable[]a,int k,int n){
- while(2*k<=n){
- int j = 2*k;
- if(less(j,j+1)) j++;//找到两个子节点中值比较大的那个
- if(!less(a,k,j)) break;
- exch(a,k,j);
- k = j;
- }
- }
- //对a数组中的第k个点和第n个点进行位置对换
- private static void exch(Comparable[]a,int k,int n){
- Comparable temp;
- temp = a[k];
- a[k] = a[n];
- a[n] = temp;
- }
- private boolean less(Comparable[]a,int i,int j){
- //return a[i]<a[j]?true:false;
- return a[i].compareTo(a[j])<0;
- }
7.堆排序的运行时间
将N个元素排序,堆排序只需要少于(2NlgN + 2N)次比较(以及一半次数的交换)。
其中2N项来自于堆的构造,2NlgN项来自于每次下沉操作最大可能需要2lgN次比较。