基于堆的优先队列和堆排序

1.什么是优先队列
         优先队列就是具备优先级的队列,优先级可以用队列里面的值大小表示,也就是值越大表示的优先级越高,所以,优先队列就是可以删除最大元素和插入元素的队列。
2.什么是堆
          堆又称为二叉堆, 二叉堆是一组能够用堆有序的完全二叉树排序的元素,并在数组中按照层次存储(不使用数组的第一个位置)。
3.为什么要使用堆来实现优先队列
          由于优先队列需要对其存储的数据进行大小排列(即数据的优先级大小),使用二叉堆对队列进行排序后,优先队列的插入元素和删除最大元素的最坏情况的运行时间都是logN级别的,运行时间很小,所以使用二叉堆实现优先队列。
          为什么 二叉堆对队列进行排序后, 插入元素和删除最大元素的最坏情况的运行时间都是logN级别?
          由于优先队列是用二叉堆实现的,当插入元素时,其只需要比较是否比自己的根节点大,而不需要与根节点的同一级别的节点进行比较,也就相当于少比较了将近一半二叉树的数据,因此是logN级别的运行时间。当删除最大元素时,需要先将最大元素的值与最后一个节点的位置互换,然后再对互换后的二叉堆进行排序,原理与插入元素一样,都只需要比较一半的二叉堆数据。
4.怎样用二叉堆对队列元素进行排序(即插入元素和删除最大元素)
          (1)插入元素
               插入元素的代码实现用swim(n)方法,其实现如下:
                  
  1.                //判断插入的元素是否比它的根节点元素大
  2.                //如果比根节点大,就与根节点互换位置,根节点的下标为n/2
  3.            private void swim(int n){
  4.                   while(n>1&&less(n/2,n)){
  5.                       exch(n/2,n);
  6.                       n=n/2;
  7.                   }
  8.               }

          (2)删除最大元素
               删除最大元素首先得将根节点与最后一个子节点的位置互换,然后再实现删除操作,最后再通过delMax()方法实现删除后的队列排序。delMax()方法的实现代码如下:

  1.           private int delMax(){
  2.                int max = pq[1];//pq[]数组用于存储优先队列的值,pq[1]就是最大值,不适用pq[0]
  3.                exch(1,N--);//交换最大元素和最后一个节点
  4.                pq[N+1]=null;//删除最大元素,防止对象游离
  5.                sink(1);//对优先队列重新排序
  6.                return max;//返回删除的最大元素
  7.           }
               上述代码使用的sink(k)方法的实现如下:

  1.           private void sink(int k){
  2.                while(k*2<=N){
  3.                     int j = k*2;
  4.                     if(less(j,j+1)) j++;
  5.                     if(!less(k,j)) break;
  6.                     exch(k,j);
  7.                     k=j;
  8.                }
  9.           }
5.用二叉堆实现的优先队列的全部代码如下:

  1.           public class MaxPQ>{
  2.                private Key[] pq;
  3.                private int N = 0;
  4.                
  5.                pulbic MaxPQ(int maxN){
  6.                     //值存储在pq[1...N]中,pq[0]不适用,所以数组的长度为maxN+1
  7.                     pq = (Key[]) new Comparable[maxN+1];
  8.                }

  9.                public boolean isEmpty(){
  10.                     return N==0;
  11.                }

  12.                public int size(){
  13.                     return N;
  14.                }

  15.                public void insert(Key v){
  16.                     pq[++N] = v;
  17.                     swim(N);
  18.                }

  19.                public Key delMax(){
  20.                     Key max = pq[1];
  21.                     exch(q,N--);
  22.                     pq[N+1]=null;
  23.                     sink(1);
  24.                     return max;
  25.                }
  26.                //上浮
  27.                private void swim(int n){
  28.                     while(n>1&&less(n/2,n)){
  29.                          exch(n/2,n);
  30.                          n=n/2;
  31.                     }
  32.                }
  33.                //下沉
  34.                private void sink(int k){
  35.                     while(k*2<=N){
  36.                          int j = k*2;
  37.                          if(less(j,j+1)) j++;
  38.                          if(!less(k,j)) break;
  39.                          exch(k,j);
  40.                          k=j;
  41.                     }
  42.                }

  43.                private boolean less(int i,int j){
  44.                     //return pq[i]
  45.                     return pq[i].compareTo(pq[j])<0;
  46.                }

  47.                private void exch(int i,int j){
  48.                     Comparable temp;
  49.                     temp = pq[i];
  50.                     pq[i] = pq[j];
  51.                     pq[j] = temp;
  52.                }
  53.                
  54.           }
6.堆排序
     (1)什么是堆排序
               堆排序就是采用基于堆的优先队列对需要排序的数据进行排序,排序规则是按照从小到大排序的。
     (2)堆排序的基本思想
               由于基于堆的优先队列是按照从大到小进行排序的,所以可以采用基于堆的优先队列的delMax()方法的思想,在删
        除元素之前将最大值与最后一个节点进行位置的互换,再将最大值删除。根据这个思想,可以每次将已排序好的优先队
        列的最大值于最后一个节点互换,换完位置后这个优先队列的长度就缩减1,然后再对新的优先队列采用sink()方法进行
        重新排序。以此类推,可以将整个优先队列按照从小到大排序。
     (3)堆排序的代码实现
               
  1.                private static void heapSort(Comparable[] a){
  2.                      //对任意顺序的数组a用基于堆的优先队列方法对其进行排序
  3.                      int n = a.length;
  4.                      for(int k=n/2;k>=1;k--){
  5.                          //一个根节点一个根节点的进行排序
  6.                          sink(a,k,n);
  7.                      }

  8.                      //对排序好的优先队列按照从小到大进行排序
  9.                      while(n>1){
  10.                          //先将最大值与最后一个节点位置进行互换
  11.                          exch(a,1,n--);
  12.                          //对新的队列进行排序
  13.                          sink(a,1,n);
  14.                      }
  15.                }

  16.                     //对数组a所实现的优先队列,从第k个节点到第n个节点进行排序
  17.                     private static void sink(Comparable[]a,int k,int n){
  18.                          while(2*k<=n){
  19.                               int j = 2*k;
  20.                               if(less(j,j+1)) j++;//找到两个子节点中值比较大的那个
  21.                               if(!less(a,k,j)) break;
  22.                               exch(a,k,j);
  23.                               k = j;
  24.                          }
  25.                     }

  26.                     //对a数组中的第k个点和第n个点进行位置对换
  27.                     private static void exch(Comparable[]a,int k,int n){
  28.                          Comparable temp;
  29.                          temp = a[k];
  30.                          a[k] = a[n];
  31.                          a[n] = temp;
  32.                     }

  33.                     private boolean less(Comparable[]a,int i,int j){
  34.                          //return a[i]
  35.                          return a[i].compareTo(a[j])<0;                         
  36.                     }
7.堆排序的运行时间
          将N个元素排序,堆排序只需要少于(2NlgN  +  2N)次比较(以及一半次数的交换)。
          其中2N项来自于堆的构造,2NlgN项来自于每次下沉操作最大可能需要2lgN次比较。




你可能感兴趣的:(算法)