数据结构实现之最小索引优先队列

来源:互联网 发布:朱乒乓网络课 编辑:程序博客网 时间:2024/04/28 14:04

在很多应用中,允许用例引用进入优先队列中的元素是有必要的。做到这一点的一种简单方式是给每个元素一个索引。
另外,一种常见的情况是用例已经有了总量为N的多个元素,而且可能还同时使用了多个(平行)数组来存储这些元素的信息。此时,其他无关的用例代码可能已经在使用一个整数索引来引用这些无关元素了。这些考虑引导我们设计了如下所示的API。

public class IndexMinPQ> 说明 比较次数的增长数量级 IndexMinPQ(int maxN) 创建一个最大容量为maxN的优先队列,索引的取值范围为0至maxN-1 void insert(int i,Key key) 插入一个元素,将它和索引k相关联 logN void changeKey(int i,Key key) 将索引为i的元素设为key logN boolean contains(int i) 是否存在索引为k的元素 1 void delete(int i) 删除索引i及其相关联的元素 logN Key minKey() 返回最小元素 1 int minIndex() 返回最小元素的索引 1 int delMin() 删除最小元素并返回它的索引 logN boolean isEmpty() 优先队列是否为空 1 int size() 优先队列的元素数量 1

理解这种数据结构的一个较好方法是将它看成一个能够快速访问其中最小元素的数组。事实上它还要更好——它能够快速访问数组的一个特定子集中的最小元素(指所有被插入的元素)。换句话说,可以将名为pq的IndexMinPQ类优先队列看做数组pq[0…N-1]中的一部分元素的代表。将pq.insert(i,key)看做将i加入这个子集并使pq[i] = key,pq.changeKey(i,key)则代表令pq[i]=key。这两种操作没有改变其他操作所依赖的数据结构,其中最重要的就是delMin()(删除最小元素并返回min的索引)和changeKey(i,key)(改变数据结构中没有索引对应的键值即pq[i]=key)。这些操作在许多应用中都有很重要并且依赖于对元素的引用(索引)。
一般来说,当堆发生变化时,我们可以用adjustDown(向下调整堆)或adjustUp(向上调整堆)来恢复堆的有序性。在这些操作中,我们可以用索引查找元素。能够定位堆中的任意元素也使我们在API中加入一个delete(int i)操作。

索引优先队列用例见索引优先队列用例(多路归并)

package xwq.dt;import java.util.Iterator;import java.util.NoSuchElementException;import xwq.util.StdOut;import xwq.util.StdRandom;/** * 使用二叉堆实现的索引最小优先队列 * class IndexMinPQ是一个支持泛型的索引优先队列。 * IndexMinPQ支持普通的insert、delete-the-minimum、delete以及change-the-key方法。 * 用户可以使用队列中的0到maxN-1号索引执行删除和修改方法。 * IndexMinPQ支持获取队列最小元素,队列最小元素索引操作。 * IndexMinPQ支持迭代器迭代所有插入的索引号。 *  * IndexMinPQ的实现使用二叉堆。 *  The <em>insert</em>, <em>delete-the-minimum</em>, <em>delete</em>, *  <em>change-key</em>, <em>decrease-key</em>, and <em>increase-key</em> *  操作时间复杂度为O(lgN). *  The <em>is-empty</em>, <em>size</em>, <em>min-index</em>, <em>min-key</em>, and <em>key-of</em> *  operations 时间复杂度为O(1). * @author xwq * * @param <Key> */public class IndexMinPQ <Key extends Comparable<Key>> implements Iterable<Integer> {    private int maxN; //索引优先队列中元素的最大个数    private int N; //当前索引优先队列中元素的个数    private int[] pq;//使用一级索引的二叉堆    private int[] qp;//pq的对称映射 qp[pq[i]] = pq[qp[i]] = i,用于映射key索引对应pq二叉堆中的位置     private Key[] keys; //keys[i] = priority of i    /**     * 初始化索引区间为(0到maxN-1)的空索引优先队列     * @param capacity      */    public IndexMinPQ(int capacity) {        if(capacity <= 0)            throw new IllegalArgumentException();        maxN = capacity;        N = 0;        pq = new int[capacity+1];        qp = new int[capacity+1];        keys = (Key[])new Comparable[capacity+1];        //初始每个索引都没用过        for(int i=0;i<=maxN;i++)             qp[i] = -1;    }    /**     * 如果队列为空返回true     * @return 如果队列为空返回true,否则返回false     */    public boolean isEmpty() {        return N==0;    }    /**     * 队列的当前元素个数     * @return 队列的当前元素个数     */    public int size() {        return N;    }    /**     * 判断优先队列是否已存在索引i     * @param i 索引i     * @return 如果索引i之前已插入,返回true,否则false     */    public boolean contains(int i) {        return qp[i] != -1;    }    /**     * 返回最小值的索引号     * @return 最小值的索引号      */    public int minIndex() {        if(isEmpty())            throw new NoSuchElementException("IndexMinPQ underflow.");        return pq[1];    }    /**     * 返回优先队列最小值,即二叉堆根节点     * @return 优先队列最小值     */    public Key minKey() {        if(isEmpty())            throw new NoSuchElementException("IndexMinPQ underflow.");        return keys[pq[1]];    }    /**     * 索引i对应优先队列中的键值     * @param i 索引i     * @return 索引i对应的键值     */    public Key keyOf(int i) {        if(!contains(i))            throw new NoSuchElementException("IndexMinPQ has not contains index i");        return keys[i];    }    /**     * 将索引i与键值key关联     * @param i 索引i     * @param key 键值key     */    public void insert(int i,Key key) {        if(i<0 || i>=maxN)            throw new IllegalArgumentException("index i out of boundary.");        if(contains(i))            throw new IllegalArgumentException("index i has allocted");        N++;        qp[i] = N;        pq[N] = i;//pq,qp互为映射        keys[i] = key;        adjustUp(N);    }    /**     * 删除最小键值并返回其对应的索引     * @return 最小键值对应的索引     */    public int delMin() {        if(isEmpty())            throw new NoSuchElementException("IndexMinPQ underflow.");        int min = minIndex();        delete(min);        return min;    }    /**     * 删除索引i以及其对应的键值     * @param i 待删除的索引i     */    public void delete(int i){        if(!contains(i))            throw new NoSuchElementException("IndexMinPQ has not contains index i");        int pqi = qp[i];        swap(pqi,N--);        adjustUp(pqi);        adjustDown(pqi);        qp[i] = -1;     //删除        keys[i] = null; //便于垃圾收集        pq[N+1] = -1; //不是必须,但是加上便于理解    }    /**     * 改变与索引i关联的键值     * @param i 待改变键值的索引     * @param key 改变后的键值     */    public void changeKey(int i,Key key) {        if(!contains(i))            throw new NoSuchElementException("IndexMinPQ has not contains index i");        if(keys[i].compareTo(key) == 0)            throw new IllegalArgumentException("argument key equpal to the original value.");        if(key.compareTo(keys[i]) > 0)              increaseKey(i,key);//原键值增加        else             decreaseKey(i,key);//原键值减小    }    /**     * 减小与索引i关联的键值到给定的新键值     * @param i  与待减小的键值关联的索引     * @param key 新键值     */    public void decreaseKey(int i,Key key) {        if(!contains(i))            throw new NoSuchElementException("IndexMinPQ has not contains index i");        if(key.compareTo(keys[i]) > 0)            throw new IllegalArgumentException("argument key more than the original value.");        keys[i] = key;        int pqi = qp[i];        adjustUp(pqi);        adjustDown(pqi);    }    /**     * 增加与索引i关联的键值到给定的新键值     * @param i 与待增加的键值关联的索引     * @param key 新键值     */    public void increaseKey(int i,Key key) {        if(!contains(i))            throw new NoSuchElementException("IndexMinPQ has not contains index i");        if(key.compareTo(keys[i])<0)            throw new IllegalArgumentException("argument key less than the original value");        keys[i] = key;        int pqi = qp[i];        adjustUp(pqi);        adjustDown(pqi);    }    /***************************************************************************        * General helper functions.    ***************************************************************************/    /**     * 交换一级索引值,以及其对称映射中的值     * @param i 使用一级索引的二叉堆的索引i     * @param j 使用一级索引的二叉堆的索引j     */    private void swap(int i,int j) {        int t = pq[i]; pq[i] = pq[j]; pq[j] = t;        int qpi = pq[i];         int qpj = pq[j];        qp[qpi] = i;        qp[qpj] = j;    }    /**     * 判断键值的大小关系     * @param i 使用一级索引的二叉堆的索引i     * @param j 使用一级索引的二叉堆的索引j     * @return keys[ki] < keys[kj] 返回true,否则返回false      */    private boolean less(int i,int j) {        int ki = pq[i];        int kj = pq[j];        return keys[ki].compareTo(keys[kj]) < 0;    }    /***************************************************************************        * Heap helper functions.     ***************************************************************************/    /**     * 向下调整最小二叉堆     * @param i 一级索引的二叉堆的索引i,pq数组的数组位置     */    private void adjustDown(int i) {        while(2*i <= N) {            int l = 2*i;            while(l<N && less(l+1,l))                l++;            swap(l,i);            i = l;        }    }    /**     * 向上调整最小二叉堆     * @param i 一级索引的二叉堆的索引i,pq数组的数组位置     */    private void adjustUp(int i) {        while(i>1) {            int p = i/2;            if(less(p,i))                break;            swap(p,i);             i = p;        }    }    @Override    public Iterator<Integer> iterator() {        return new HeapIterator();    }    /***************************************************************************        * Iterators.     ***************************************************************************/    /**     * Returns an iterator that iterates over the keys on the     * priority queue in ascending order.     * The iterator doesn't implement <tt>remove()</tt> since it's optional.     *     * @return an iterator that iterates over the keys in ascending order     */    private class HeapIterator implements Iterator<Integer> {        // create a new pq        IndexMinPQ<Key> copy;        // add all elements to copy of heap        // takes linear time since already in heap order so no keys move        public HeapIterator() {            copy = new IndexMinPQ<Key>(maxN);            for(int i=1;i<=N;i++) {                int ki = pq[i];                  Key key = keys[ki];                copy.insert(ki, key);            }        }        @Override        public boolean hasNext() {            return !copy.isEmpty();        }        @Override        public Integer next() {            if(!hasNext())                 throw new NoSuchElementException("IndexMinPQ underflow.");            return copy.delMin();        }        @Override        public void remove() {            throw new UnsupportedOperationException("unsupported remove operation.");        }    }    /**     * Unit tests the <tt>IndexMinPQ</tt> data type.     */    public static void main(String[] args) {        // insert a bunch of strings        String[] strings = { "it", "was", "the", "best", "of", "times", "it", "was", "the", "worst" };        IndexMinPQ<String> pq = new IndexMinPQ<String>(strings.length);        for (int i = 0; i < strings.length; i++) {            pq.insert(i, strings[i]);        }               StdOut.print();        // print each key using the iterator        for (Integer i : pq) {            StdOut.println(i + " " + pq.keyOf(i));        }        StdOut.println();     // increase or decrease the key        for (int i = 0; i < strings.length; i++) {            if (StdRandom.uniform() < 0.5)                pq.increaseKey(i, strings[i] + strings[i]);            else                pq.decreaseKey(i, strings[i].substring(0, 1));        }        // delete and print each key        while (!pq.isEmpty()) {            String key = pq.minKey();            int i = pq.delMin();            StdOut.println(i + " " + key);        }        StdOut.println();     // reinsert the same strings        for (int i = 0; i < strings.length; i++) {            pq.insert(i, strings[i]);        }        // delete them in random order        int[] perm = new int[strings.length];        for (int i = 0; i < strings.length; i++)            perm[i] = i;        StdRandom.shuffle(perm);        for (int i = 0; i < perm.length; i++) {            String key = pq.keyOf(perm[i]);            pq.delete(perm[i]);            StdOut.println(perm[i] + " " + key);        }    }}
0 0
原创粉丝点击