• 排序---优先队列


    写在前面的话:

    一枚自学Java和算法的工科妹子。

    • 算法学习书目:算法(第四版) Robert Sedgewick
    • 算法视频教程:Coursera  Algorithms Part1&2

    本文是根据《算法(第四版)》的个人总结,如有错误,请批评指正。

    一、优先队列介绍

        优先队列是0个或多个元素的集合,每个元素都有一个优先权或值,对优先队列执行的操作有1) 插入元素;2) 删除最大(最小)元素.

        在最小优先队列(min priority queue)中,删除操作用来删除优先权最小的元素;在最大优先队列(max priority queue)中,删除操作用来删除优先权最大的元素。

        优先队列的使用和队列(删除最老的元素)以及栈(删除最新的元素)类似,但高效的实现它则更具有挑战性。

        优先队列是一种抽象数据类型,它表示了一组值和对这些值的操作。优先队列(最大优先队列为例)的API设计如下:

         设计优先队列API,我们想实现的功能:输入N个字符串(N非常大),每个字符串都对应着一个整数,目标就是从中找出最大的(或最小的)M个整数及其关联的字符串。在上述API中着重要关注的方法就是插入insert()和删除最大元素delMax().

    解决方法:

        1.将输入排序然后从中找出M个最大的元素,但是因为输入元素非常庞大,造成存储空间的浪费;

        2.将每个新的输入和已知的M个最大元素对比,这会导致比较的时间复杂度为NM;

        3.以下会介绍基于堆的优先队列实现,降低时间和空间复杂度。

    表1 从N个输入中找到最大的M个元素所需的成本

    示     例 时间复杂度 空间复杂度
    排序算法的用例(方法一) NlgN N
    初级实现的优先队列(方法二) NM M
    基于堆的优先队列(方法三) NlgM M

    优先队列的初级实现:

    1.数组实现(无序)

    • 插入操作:和栈的push()方法完全一样;
    • 删除操作:添加一段类似于选择排序的内循环,将最大元素和边界元素交换,然后删除,和pop()操作一样;
     1 public class UnorderedMaxPQ<Key extends Comparable<Key>>
     2 {
     3     private Key[] pq; // pq[i] = ith element on pq
     4     private int N; // number of elements on pq
     5     
     6     public UnorderedMaxPQ(int capacity){ 
     7          pq = (Key[]) new Comparable[capacity]; 
     8     }
     9 
    10     public boolean isEmpty(){ return N == 0; }
    11     
    12     public void insert(Key x){ pq[N++] = x; }
    13    
    14     public Key delMax(){
    15         int max = 0;
    16         for (int i = 1; i < N; i++)
    17             if (less(max, i)) max = i;
    18         exch(max, N-1);
    19         return pq[--N];
    20     }
    21 }

    2.数组实现(有序)

    • 插入操作:将所有较大的元素向右边移动一个使数组保持有序,类似于插入排序;
    • 删除操作:和栈的pop()操作一样;

    表2 优先队列的各种事先在最坏情况下运行时间的增长数量级

        数据结构       插入元素     删除最大元素  
    无序数组 N 1
    有序数组 1 N
    lgN lgN

     

    二、二叉堆的介绍

        在二叉堆数组中,每个元素都要保证大于等于另两个特定位置的元素。从a[k]向上一层就令k=k/2,向下一层就令K=2k或2k+1。一颗大小为N的完全二叉树的高度为lgN向下取整。

     

        堆有序化操作:

    (1)由下至上的堆有序化(上浮)   

    1 private void swim(int k) {
    2    while (k > 1 && less(k/2, k)) {
    3       exch(k, k/2);
    4       k = k/2;
    5    }
    6 }

    (2)由上至下的堆有序化(下沉)

    1 private void sink(int k) {
    2    while (2*k <= N) {
    3       int j = 2*k;
    4       if (j < N && less(j, j+1)) j++;
    5       if (!less(k, j)) break;
    6       exch(k, j);
    7       k = j;
    8    }
    9 }

    三、基于堆的优先队列

    1.如何实现?

    •     优先队列有一个基于堆的完全二叉树表示,存储于数组pq[1..N]中,pq[0]没有使用。
    •     在insert()中,我们将N加1并把新元素添加在数组最后,然后用swim()恢复堆的秩序。
    •     在delete()中,我们从pq[1]中得到需要的返回元素,然后将pq[N]移动到pq[1],将N减1并使用sink()恢复堆的秩序。
    •     同时我们还将不在使用的pq[N+1]设置为null,以便系统回收删除元素所占用的空间,也使用了动态调整数组大小的方法,具体见下压栈(LILO)详解

    2.性能分析

        对于一个含有N个元素的基于堆的优先队列,插入元素操作只需不超过(lgN+1)次比较,删除最大元素的操作需要不超过2lgN次比较,以下是代码实现:

      1 import java.util.Comparator;
      2 import java.util.Iterator;
      3 import java.util.NoSuchElementException;
      4 
      5 public class MaxPQ<Key> implements Iterable<Key> {
      6     private Key[] pq;                    // 完全二叉树
      7     private int n;                      // 优先队列中元素数目
      8     private Comparator<Key> comparator;  // optional Comparator
      9 
     10     public MaxPQ(int initCapacity) {
     11         pq = (Key[]) new Object[initCapacity + 1];
     12         n = 0;       // pq[0]没有使用
     13     }
     14 
     15     public MaxPQ() { this(1);}
     16 
     17     public MaxPQ(int initCapacity, Comparator<Key> comparator) {
     18         this.comparator = comparator;
     19         pq = (Key[]) new Object[initCapacity + 1];
     20         n = 0;
     21     }
     22 
     23     public MaxPQ(Comparator<Key> comparator) {
     24         this(1, comparator);
     25     }
     26 
     27     public MaxPQ(Key[] keys) {
     28         n = keys.length;
     29         pq = (Key[]) new Object[keys.length + 1]; 
     30         for (int i = 0; i < n; i++)
     31             pq[i+1] = keys[i];
     32         for (int k = n/2; k >= 1; k--)
     33             sink(k);
     34         assert isMaxHeap();
     35     }
     36       
     37     public boolean isEmpty() {return n == 0; }
     38 
     39     public int size() {return n;}
     40 
     41     public Key max() {
     42         if (isEmpty()) throw new NoSuchElementException("Priority queue underflow");
     43         return pq[1];
     44     }
     45 
     46     private void resize(int capacity) {
     47         assert capacity > n;
     48         Key[] temp = (Key[]) new Object[capacity];
     49         for (int i = 1; i <= n; i++) {
     50             temp[i] = pq[i];
     51         }
     52         pq = temp;
     53     }
     54 
     55     public void insert(Key x) {
     56 
     57         // double size of array if necessary
     58         if (n >= pq.length - 1) resize(2 * pq.length);
     59 
     60         // add x, and percolate it up to maintain heap invariant
     61         pq[++n] = x;
     62         swim(n);
     63         assert isMaxHeap();
     64     }
     65 
     66     public Key delMax() {
     67         if (isEmpty()) throw new NoSuchElementException("Priority queue underflow");
     68         Key max = pq[1];
     69         exch(1, n--);
     70         sink(1);
     71         pq[n+1] = null;     // to avoid loiterig and help with garbage collection
     72         if ((n > 0) && (n == (pq.length - 1) / 4)) resize(pq.length / 2);
     73         assert isMaxHeap();
     74         return max;
     75     }
     76 
     77     private void swim(int k) {
     78         while (k > 1 && less(k/2, k)) {
     79             exch(k, k/2);
     80             k = k/2;
     81         }
     82     }
     83 
     84     private void sink(int k) {
     85         while (2*k <= n) {
     86             int j = 2*k;
     87             if (j < n && less(j, j+1)) j++;
     88             if (!less(k, j)) break;
     89             exch(k, j);
     90             k = j;
     91         }
     92     }
     93 
     94     private boolean less(int i, int j) {
     95         if (comparator == null) {
     96             return ((Comparable<Key>) pq[i]).compareTo(pq[j]) < 0;
     97         }
     98         else {
     99             return comparator.compare(pq[i], pq[j]) < 0;
    100         }
    101     }
    102 
    103     private void exch(int i, int j) {
    104         Key swap = pq[i];
    105         pq[i] = pq[j];
    106         pq[j] = swap;
    107     }
    108 
    109     // is pq[1..N] a max heap?
    110     private boolean isMaxHeap() {
    111         return isMaxHeap(1);
    112     }
    113 
    114     // is subtree of pq[1..n] rooted at k a max heap?
    115     private boolean isMaxHeap(int k) {
    116         if (k > n) return true;
    117         int left = 2*k;
    118         int right = 2*k + 1;
    119         if (left  <= n && less(k, left))  return false;
    120         if (right <= n && less(k, right)) return false;
    121         return isMaxHeap(left) && isMaxHeap(right);
    122     }
    123 
    124     public Iterator<Key> iterator() {
    125         return new HeapIterator();
    126     }
    127 
    128     private class HeapIterator implements Iterator<Key> {
    129 
    130         // create a new pq
    131         private MaxPQ<Key> copy;
    132 
    133         // add all items to copy of heap
    134         // takes linear time since already in heap order so no keys move
    135         public HeapIterator() {
    136             if (comparator == null) copy = new MaxPQ<Key>(size());
    137             else                    copy = new MaxPQ<Key>(size(), comparator);
    138             for (int i = 1; i <= n; i++)
    139                 copy.insert(pq[i]);
    140         }
    141 
    142         public boolean hasNext()  { return !copy.isEmpty();                     }
    143         public void remove()      { throw new UnsupportedOperationException();  }
    144 
    145         public Key next() {
    146             if (!hasNext()) throw new NoSuchElementException();
    147             return copy.delMax();
    148         }
    149     }
    150 }

    3.堆排序

     1 public class Heap
     2 {
     3     public static void sort(Comparable[] a){
     4        int N = a.length;
     5        for (int k = N/2; k >= 1; k--) sink(a, k, N);
     6        
     7        while (N > 1){
     8        exch(a, 1, N);
     9         sink(a, 1, --N);
    10         }
    11     }
    12    
    13      private static void sink(Comparable[] a, int k, int N)
    14      { /* as before */ }
    15      private static boolean less(Comparable[] a, int i, int j)
    16      { /* as before */ }
    17      private static void exch(Comparable[] a, int i, int j)
    18      { /* as before */ }
    19 }

    作者: 邹珍珍(Pearl_zhen)

    出处: http://www.cnblogs.com/zouzz/

    声明:本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出 原文链接 如有问题, 可邮件(zouzhenzhen@seu.edu.cn)咨询.

  • 相关阅读:
    第九十一天 how can I 坚持 技术-永远的技术
    第九十天 how can I 坚持
    Java控制台中输入中文输出乱码的解决办法
    【体系结构】转移预测器设计与比较1
    Ubuntu 13.04 用Sublime Text 2 编译运行 JAVA
    HDU 4605 Magic Ball Game (在线主席树|| 离线 线段树)
    个人重构机房收费系统之报表
    快速排序的递归和非递归实现
    HDU 3721 Building Roads (2010 Asia Tianjin Regional Contest)
    体验决定深度,知识决定广度。你的人生是什么呢? 操蛋和扯蛋没必要纠结 唯有继续
  • 原文地址:https://www.cnblogs.com/zouzz/p/6098893.html
Copyright © 2020-2023  润新知