zoukankan      html  css  js  c++  java
  • 索引式优先队列(indexed priority queue)

    为了达到O(ElogV)的效率,需要对普利姆算法进行eager实现。
    如果我们用java来做,jdk当中的priorityQueue并不能满足我们的要求。
    因为我们需要进行一个对索引元素降key的操作(decrease-key).

    /**
         * 将索引所关联的key降到newKey
         *
         * @param index 索引
         * @param newKey 新的key
         */
        public void decreaseKey(int index, E newKey) {
            if (index < 0 || index >= queue.length)
                throw new IndexOutOfBoundsException();
            if (newKey == null)
                throw new NullPointerException();
            if (!contains(index))
                throw new NoSuchElementException("指定的索引不存在!");
            if (this.comparator() != null) {
                if (this.comparator().compare((E) keys[index], newKey) <= 0)
                    throw new IllegalArgumentException("指定的key必须小于原索引关联的key!");
            } else {
                Comparable<? super E> key = (Comparable<? super E>) getKeyOf(index);
                if (key.compareTo(newKey) <= 0)
                    throw new IllegalArgumentException("指定的key必须小于原索引关联的key!");
            }
    
            keys[index] = newKey;
            siftUp(getPositionOf(index));
        }

    由于需要知道优先队列中元素的索引以支持外部索引式访问,我们将对priorityQueue中的Queue建立索引,并将此索引当作二叉堆实际存储的元素,而原来的元素我们通过索引表来访问它.

        /**
         * 索引所关联的keys
         */
        private transient Object[] keys;
        /**
         * 建立一个从索引在二叉堆中的位置到索引的映射
         */
        private transient int[] queue;

    这里还有一个问题,就是在进行上浮(siftUp)和下沉(siftDown)时,需要对堆中的元素和位置进行互访,我们还需要建立一个索引到位置的倒排,或者索引到位置的一个映射.

        /**
         * 建立一个从索引到其在二叉堆中位置的映射,下标是关联key的索引
         */
        private transient int[] postQueue;

    这样我们就可以按下面的方式访问二叉堆中的索引、它在二叉堆中的位置、以及它关联的key:

    /**
         * 返回给定位置的索引
         *
         * @param position 二叉堆中的位置
         * @return 给定位置的索引
         */
        private int getIndexOf(int position) {
            return queue[position];
        }
    
        /**
         * 返回给定索引在二叉堆中的位置
         *
         * @param index 索引
         * @return 给定索引在二叉堆中的位置
         */
        private int getPositionOf(int index) {
            return postQueue[index];
        }
        /**
         * 返回给定索引所关联的key
         *
         * @param index 索引
         * @return 索引所关联的key
         */
        private E getKeyOf(int index) {
            return (E) keys[index];
        }

    而交换元素时,需要先交换索引,然后再维护索引到位置的映射:

    /**
         * 按位置交换
         *
         * @param x 一个元素的位置
         * @param y 另一个元素的位置
         */
        private void exchangeByPosition(int x, int y) {
            int tmp = queue[x];
            queue[x] = queue[y];
            queue[y] = tmp;
    
            postQueue[queue[x]] = x;
            postQueue[queue[y]] = y;
        }

    这样,我们的上浮操作看起来是这样的:

        private void siftUpComparable(int k) {
            Comparable<? super E> key = (Comparable<? super E>) keys[getIndexOf(k)];
            while (k > 0) {
                int parent = (k - 1) >>> 1;
                Object e = keys[queue[parent]];
                if (key.compareTo((E) e) >= 0)
                    break;
                exchangeByPosition(k, parent);
                k = parent;
            }
        }

    而下沉操作就变成了这样:

    private void siftDownUsingComparator(int k) {
            E x = getKeyOf(getIndexOf(k));
            int half = size >>> 1;
            while (k < half) {
                //假设作孩子是least
                int child = (k << 1) + 1;
                Object c = keys[child];
                int right = child + 1;
                //如果右孩子更小,那就和右孩子比较
                if (right < size &&
                        comparator.compare((E) c, getKeyOf(getIndexOf(right))) > 0)
                    c = getKeyOf(getIndexOf(child = right));
                //当元素不大于它的所有孩子时停止
                if (comparator.compare(x, (E) c) <= 0)
                    break;
                //否则交换元素和它最小的孩子
                exchangeByPosition(k, child);
                //继续下沉
                k = child;
            }
        }

    既然加入了对元素的索引支持,那么入队操作就变成了这样:

        /**
         * 将index关联的key加入队列
         *
         * @param index 索引
         * @param key   key
         * @return
         */
        public boolean offer(int index, E key) {
            if (index < 0)
                throw new IndexOutOfBoundsException();
            if (key == null)
                throw new NullPointerException();
    
            int last = size;
            //是否需要扩容
            if (last >= keys.length)
                grow(last + 1);
            size = last + 1;
    
            //首先将元素加入到队尾,然后从队尾上浮,直到满足堆的不变性
    
            //index到position的映射
            postQueue[index] = last;
            //position到index的映射
            queue[last] = index;
            //保存index关联的key
            keys[index] = key;
            if(last == 0)
                return true;
            //上浮
            siftUp(last);
            return true;
        }

    相应的,出队操作也要修改:

    /**
         * 返回最优先的元素并在队列中删除此元素
         *
         * @return 最优先的元素, 如果为负,表示队列已空
         */
        public int poll() {
            if (size == 0)
                return -1;
            int tail = --size;
            int head = getIndexOf(0);
            if(tail != 0){
                exchangeByPosition(0, tail);
                siftDown(0);
            }
    
            postQueue[head] = -1;
            //队尾的元素经过交换后就是之前的队头,现在可以删除了
            keys[getIndexOf(tail)] = null;
            queue[tail] = -1;
            return head;
        }

    下面是对索引式优先队列的一个完整实现,支持最大优先队列和最小优先队列:

    
    import java.util.*;
    
    /**
     * Created by 浩然 on 4/19/15.
     * 索引式优先队列
     * <p/>
     * 建立目的:
     * 在实现普利姆算法时,需要用优先队列来优化最轻边的查找.
     * 而jdk中的优先队列只满足lazy实现,如果要作eager实现,需实现一个decrease-key操作.
     * <p/>
     * 参考:
     * 1.普林斯顿大学 algorithms 4th edition.
     * 2.jdk priorityQueue.
     * <p/>
     * 说明:
     * 1.队头是最least的元素
     * 2.可通过自定义Comparable或Comparator实现最大优先、最小优先队列.
     * 3.假设是最小优先队列,堆的不变性是指,任何插入、删除、出队、入队的操作都满足以下性质:
     * 任何一个父元素的key都不大于子元素的key
     * 而对于最大优先队列,则需满足任何一个父元素的key都不小于子元素的key
     */
    public class IndexPriorityQueue<E> {
        private static final int DEFAULT_INITIAL_CAPACITY = 11;
    
        /**
         * 索引所关联的keys
         * 以O(1)的时间找出给定索引所关联的key
         */
        private transient Object[] keys;
    
        //建立下面两个辅助字段的目的:
        //1:以O(1)的时间找到给定索引的位置
        //2:以O(1)的时间找到给定位置的索引
    
        /**
         * 建立一个从索引在二叉堆中的位置到索引的映射,下标就是索引在二叉堆中的位置
         */
        private transient int[] queue;
    
        /**
         * 建立一个从索引到其在二叉堆中位置的映射,下标是关联key的索引
         */
        private transient int[] postQueue;
    
        private int size = 0;
    
        private final Comparator<? super E> comparator;
    
        public IndexPriorityQueue() {
            this(DEFAULT_INITIAL_CAPACITY, null);
        }
    
        public IndexPriorityQueue(int initialCapacity) {
            this(initialCapacity, null);
        }
    
        public IndexPriorityQueue(int initialCapacity,
                                  Comparator<? super E> comparator) {
            if (initialCapacity < 1)
                throw new IllegalArgumentException();
            this.keys = new Object[initialCapacity];
            this.queue = new int[initialCapacity];
            this.postQueue = new int[initialCapacity];
            for (int i = 0; i < postQueue.length; i++) {
                postQueue[i] = -1;
            }
            this.comparator = comparator;
    
        }
    
        private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    
        /**
         * 扩容
         *
         * @param minCapacity 所需的最小容量
         */
        private void grow(int minCapacity) {
            int oldCapacity = keys.length;
            int newCapacity = oldCapacity + ((oldCapacity < 64) ?
                    (oldCapacity + 2) :
                    (oldCapacity >> 1));
            if (newCapacity - MAX_ARRAY_SIZE > 0)
                newCapacity = hugeCapacity(minCapacity);
            keys = Arrays.copyOf(keys, newCapacity);
            queue = Arrays.copyOf(queue, newCapacity);
            postQueue = Arrays.copyOf(postQueue, newCapacity);
        }
    
        private static int hugeCapacity(int minCapacity) {
            if (minCapacity < 0)
                throw new OutOfMemoryError();
            return (minCapacity > MAX_ARRAY_SIZE) ?
                    Integer.MAX_VALUE :
                    MAX_ARRAY_SIZE;
        }
    
        /**
         * 返回队列是否为空
         *
         * @return 队列空则返回真,否则返回假
         */
        public boolean isEmpty() {
            return size == 0;
        }
    
        /**
         * 将index关联的key加入队列
         *
         * @param index 索引
         * @param key   key
         * @return
         */
        public boolean offer(int index, E key) {
            if (index < 0)
                throw new IndexOutOfBoundsException();
            if (key == null)
                throw new NullPointerException();
    
            int last = size;
            //是否需要扩容
            if (last >= keys.length)
                grow(last + 1);
            size = last + 1;
    
            //首先将元素加入到队尾,然后从队尾上浮,直到满足堆的不变性
    
            //index到position的映射
            postQueue[index] = last;
            //position到index的映射
            queue[last] = index;
            //保存index关联的key
            keys[index] = key;
            if(last == 0)
                return true;
            //上浮
            siftUp(last);
            return true;
        }
    
        /**
         * 返回最优先元素,并且不删除此元素
         *
         * @return
         */
        public int peek() {
            if (size == 0)
                return -1;
            return getIndexOf(0);
        }
    
        /**
         * 队列中是否包含指定的元素
         *
         * @param index 索引
         * @return 如果指定的索引在队列中,返回真,否则返回假
         */
        public boolean contains(int index) {
            return getPositionOf(index) != -1;
        }
    
        /**
         * 队列的长度
         *
         * @return
         */
        public int size() {
            return size;
        }
    
        /**
         * 清空队列
         */
        public void clear() {
            for (int i = 0; i < size; i++) {
                if (postQueue[i] < 0)
                    continue;
                int index = getIndexOf(i);
                keys[index] = null;
                postQueue[index] = -1;
                queue[i] = -1;
            }
            size = 0;
        }
    
        /**
         * 返回最优先的元素并在队列中删除此元素
         *
         * @return 最优先的元素, 如果为负,表示队列已空
         */
        public int poll() {
            if (size == 0)
                return -1;
            int tail = --size;
            int head = getIndexOf(0);
            if(tail != 0){
                exchangeByPosition(0, tail);
                siftDown(0);
            }
    
            postQueue[head] = -1;
            //队尾的元素经过交换后就是之前的队头,现在可以删除了
            keys[getIndexOf(tail)] = null;
            queue[tail] = -1;
            return head;
        }
    
        private int remove(int index) {
            assert index >= 0 && index < size;
            if (!contains(index))
                return -1;
    
            int s = --size;
            int position = getPositionOf(index);
            //将堆中最后一个元素和要删除的元素交换
            exchangeByPosition(position, s);
            siftUp(position);
            siftDown(position);
            //help gc
            keys[index] = null;
            //标记在堆中已无此元素
            postQueue[index] = -1;
            return -1;
        }
    
        /**
         * 上浮元素
         *
         * @param k 上浮开始的位置
         */
        private void siftUp(int k) {
            if (comparator != null)
                siftUpUsingComparator(k);
            else
                siftUpComparable(k);
        }
    
        private void siftUpComparable(int k) {
            Comparable<? super E> key = (Comparable<? super E>) keys[getIndexOf(k)];
            while (k > 0) {
                int parent = (k - 1) >>> 1;
                Object e = keys[queue[parent]];
                if (key.compareTo((E) e) >= 0)
                    break;
                exchangeByPosition(k, parent);
                k = parent;
            }
        }
    
        private void siftUpUsingComparator(int k) {
            E key = (E) keys[getIndexOf(k)];
            while (k > 0) {
                int parent = (k - 1) >>> 1;
                Object e = keys[queue[parent]];
                //如果key不小于其父节点的key,break
                if (comparator.compare(key, (E) e) >= 0)
                    break;
                //否则交换current、parent处的元素
                exchangeByPosition(k, parent);
                //继续上浮
                k = parent;
            }
        }
    
        /**
         *
         * @param k
         */
        private void siftDown(int k) {
            if (comparator != null)
                siftDownUsingComparator(k);
            else
                siftDownComparable(k);
        }
    
        /**
         * 按位置交换
         *
         * @param x 一个元素的位置
         * @param y 另一个元素的位置
         */
        private void exchangeByPosition(int x, int y) {
            int tmp = queue[x];
            queue[x] = queue[y];
            queue[y] = tmp;
    
            postQueue[queue[x]] = x;
            postQueue[queue[y]] = y;
        }
    
        private void siftDownComparable(int k) {
            Comparable<? super E> key = (Comparable<? super E>) getKeyOf(getIndexOf(k));
            int half = size >>> 1;
            while (k < half) {
                int child = (k << 1) + 1;
                Object c = getKeyOf(getIndexOf(child));
                int right = child + 1;
                if (right < size &&
                        ((Comparable<? super E>) c).compareTo(getKeyOf(getIndexOf(right))) > 0)
                    c = getKeyOf(getIndexOf(child = right));
                if (key.compareTo((E) c) <= 0)
                    break;
                exchangeByPosition(k, child);
                k = child;
            }
        }
    
        private void siftDownUsingComparator(int k) {
            E x = getKeyOf(getIndexOf(k));
            int half = size >>> 1;
            while (k < half) {
                //假设作孩子是least
                int child = (k << 1) + 1;
                Object c = keys[child];
                int right = child + 1;
                //如果右孩子更小,那就和右孩子比较
                if (right < size &&
                        comparator.compare((E) c, getKeyOf(getIndexOf(right))) > 0)
                    c = getKeyOf(getIndexOf(child = right));
                //当元素不大于它的所有孩子时停止
                if (comparator.compare(x, (E) c) <= 0)
                    break;
                //否则交换元素和它最小的孩子
                exchangeByPosition(k, child);
                //继续下沉
                k = child;
            }
        }
    
        /**
         * 将索引所关联的key降到newKey
         *
         * @param index 索引
         * @param newKey 新的key
         */
        public void decreaseKey(int index, E newKey) {
            if (index < 0 || index >= queue.length)
                throw new IndexOutOfBoundsException();
            if (newKey == null)
                throw new NullPointerException();
            if (!contains(index))
                throw new NoSuchElementException("指定的索引不存在!");
            if (this.comparator() != null) {
                if (this.comparator().compare((E) keys[index], newKey) <= 0)
                    throw new IllegalArgumentException("指定的key必须小于原索引关联的key!");
            } else {
                Comparable<? super E> key = (Comparable<? super E>) getKeyOf(index);
                if (key.compareTo(newKey) <= 0)
                    throw new IllegalArgumentException("指定的key必须小于原索引关联的key!");
            }
    
            keys[index] = newKey;
            siftUp(getPositionOf(index));
        }
    
        /**
         * 将索引所关联的key升到newKey
         *
         * @param index 索引
         * @param newKey 新的key
         */
        public void increaseKey(int index, E newKey) {
            if (index < 0 || index >= queue.length)
                throw new IndexOutOfBoundsException();
            if (newKey == null)
                throw new NullPointerException();
            if (!contains(index))
                throw new NoSuchElementException("指定的索引不存在!");
            if (this.comparator() != null) {
                if (this.comparator().compare((E) keys[index], newKey) >= 0)
                    throw new IllegalArgumentException("指定的key必须大于原索引关联的key!");
            } else {
                Comparable<? super E> key = (Comparable<? super E>) getKeyOf(index);
                if (key.compareTo(newKey) >= 0)
                    throw new IllegalArgumentException("指定的key必须大于原索引关联的key!");
            }
            keys[index] = newKey;
            siftDown(getPositionOf(index));
        }
    
        /**
         * 调整队列以保证堆的不变性
         */
        private void heapify() {
            for (int i = (size >>> 1) - 1; i >= 0; i--)
                siftDown(i);
        }
    
        public Comparator<? super E> comparator() {
            return comparator;
    
        }
    
        /**
         * 返回给定位置的索引
         *
         * @param position 二叉堆中的位置
         * @return 给定位置的索引
         */
        private int getIndexOf(int position) {
            return queue[position];
        }
    
        /**
         * 返回给定索引在二叉堆中的位置
         *
         * @param index 索引
         * @return 给定索引在二叉堆中的位置
         */
        private int getPositionOf(int index) {
            return postQueue[index];
        }
        /**
         * 返回给定索引所关联的key
         *
         * @param index 索引
         * @return 索引所关联的key
         */
        private E getKeyOf(int index) {
            return (E) keys[index];
        }
    }
    
    【版权所有@foreach_break】 【博客地址 http://www.cnblogs.com/foreach-break】 可以转载,但必须注明出处并保持博客超链接
  • 相关阅读:
    CSAPP阅读笔记-struct, union, 数据对齐-来自第三章3.9的笔记-P183-P191
    CSAPP阅读笔记-数组分配与访问-来自第三章3.8的笔记-P176-P183
    深入理解静态方法和实例化方法的区别
    通俗讲解静态方法和实例方法的区别
    ArcGis中的类模型图目录
    C++ Primer(第四版) 课后习题6.8 统计空格制表符换行的数目
    C++ Primer(第四版) 课后习题4.30
    string类sizeof大小
    C++ Primer(第四版) 课后习题4.18
    C++ Primer(第四版) 课后习题3.14 vector单词转大写
  • 原文地址:https://www.cnblogs.com/foreach-break/p/4471195.html
Copyright © 2011-2022 走看看