/****************************************************************************** * Compilation: javac IndexMultiwayMinPQ.java * Execution: * * An inde multiway heap. * ******************************************************************************/ package edu.princeton.cs.algs4; import java.util.Comparator; import java.util.Iterator; import java.util.NoSuchElementException; /** * The IndexMultiwayMinPQ class represents an indexed priority queue of generic keys. * It supports the usual insert and delete-the-minimum operations, * along with delete and change-the-key methods. * In order to let the client refer to keys on the priority queue, * an integer between 0 and N-1 is associated with each key ; the client * uses this integer to specify which key to delete or change. * It also supports methods for peeking at the minimum key, * testing if the priority queue is empty, and iterating through * the keys. * * This implementation uses a multiway heap along with an array to associate * keys with integers in the given range. * For simplified notations, logarithm in base d will be referred as log-d * The delete-the-minimum, delete, change-key and increase-key operations * take time proportional to d*log-d(n) * The insert and decrease-key take time proportional to log-d(n) * The is-empty, min-index, min-key, size, contains and key-of operations take constant time. * Construction takes time proportional to the specified capacity. * * The arrays used in this structure have the first d indices empty, * it apparently helps with caching effects. * * @author Tristan Claverie */ public class IndexMultiwayMinPQ implements Iterable { private final int d; //Dimension of the heap private int n; //Number of keys currently in the queue private int nmax; //Maximum number of items in the queue private int[] pq; //Multiway heap private int[] qp; //Inverse of pq : qp[pq[i]] = pq[qp[i]] = i private Key[] keys; //keys[i] = priority of i private final Comparator comp; //Comparator over the keys /** * Initializes an empty indexed priority queue with indices between {@code 0} to {@code N-1} * Worst case is O(n) * @param N number of keys in the priority queue, index from {@code 0} to {@code N-1} * @param D dimension of the heap * @throws java.lang.IllegalArgumentException if {@code N < 0} * @throws java.lang.IllegalArgumentException if {@code D < 2} */ public IndexMultiwayMinPQ(int N, int D) { if (N < 0) throw new IllegalArgumentException("Maximum number of elements cannot be negative"); if (D < 2) throw new IllegalArgumentException("Dimension should be 2 or over"); this.d = D; nmax = N; pq = new int[nmax+D]; qp = new int[nmax+D]; keys = (Key[]) new Comparable[nmax+D]; for (int i = 0; i < nmax+D; qp[i++] = -1); comp = new MyComparator(); } /** * Initializes an empty indexed priority queue with indices between {@code 0} to {@code N-1} * Worst case is O(n) * @param N number of keys in the priority queue, index from {@code 0} to {@code N-1} * @param D dimension of the heap * @param C a Comparator over the keys * @throws java.lang.IllegalArgumentException if {@code N < 0} * @throws java.lang.IllegalArgumentException if {@code D < 2} */ public IndexMultiwayMinPQ(int N, Comparator C, int D) { if (N < 0) throw new IllegalArgumentException("Maximum number of elements cannot be negative"); if (D < 2) throw new IllegalArgumentException("Dimension should be 2 or over"); this.d = D; nmax = N; pq = new int[nmax+D]; qp = new int[nmax+D]; keys = (Key[]) new Comparable[nmax+D]; for (int i = 0; i < nmax+D; qp[i++] = -1); comp = C; } /** * Whether the priority queue is empty * Worst case is O(1) * @return true if the priority queue is empty, false if not */ public boolean isEmpty() { return n == 0; } /** * Does the priority queue contains the index i ? * Worst case is O(1) * @param i an index * @throws java.lang.IllegalArgumentException if the specified index is invalid * @return true if i is on the priority queue, false if not */ public boolean contains(int i) { if (i < 0 ||i >= nmax) throw new IllegalArgumentException(); return qp[i+d] != -1; } /** * Number of elements currently on the priority queue * Worst case is O(1) * @return the number of elements on the priority queue */ public int size() { return n; } /** * Associates a key with an index * Worst case is O(log-d(n)) * @param i an index * @param key a Key associated with i * @throws java.lang.IllegalArgumentException if the specified index is invalid * @throws java.lang.IllegalArgumentException if the index is already in the queue */ public void insert(int i, Key key) { if (i < 0 || i >= nmax) throw new IllegalArgumentException(); if (contains(i)) throw new IllegalArgumentException("Index already there"); keys[i+d] = key; pq[n+d] = i; qp[i+d] = n; swim(n++); } /** * Gets the index associated with the minimum key * Worst case is O(1) * @throws java.util.NoSuchElementException if the priority queue is empty * @return the index associated with the minimum key */ public int minIndex() { if (isEmpty()) throw new NoSuchElementException("Priority queue is empty"); return pq[d]; } /** * Gets the minimum key currently in the queue * Worst case is O(1) * @throws java.util.NoSuchElementException if the priority queue is empty * @return the minimum key currently in the priority queue */ public Key minKey() { if (isEmpty()) throw new NoSuchElementException("Priority queue is empty"); return keys[pq[d]+d]; } /** * Deletes the minimum key * Worst case is O(d*log-d(n)) * @throws java.util.NoSuchElementException if the priority queue is empty * @return the index associated with the minimum key */ public int delMin() { if (isEmpty()) throw new NoSuchElementException("Priority queue is empty"); int min = pq[d]; exch(0, --n); sink(0); qp[min+d] = -1; keys[pq[n+d]+d] = null; pq[n+d] = -1; return min; } /** * Gets the key associated with index i * Worst case is O(1) * @param i an index * @throws java.lang.IllegalArgumentException if the specified index is invalid * @throws java.lang.IllegalArgumentException if the index is not in the queue * @return the key associated with index i */ public Key keyOf(int i) { if (i < 0 || i >= nmax) throw new IllegalArgumentException(); if (! contains(i)) throw new NoSuchElementException("Specified index is not in the queue"); return keys[i+d]; } /** * Changes the key associated with index i to the given key * If the given key is greater, Worst case is O(d*log-d(n)) * If the given key is lower, Worst case is O(log-d(n)) * @param i an index * @param key the key to associate with i * @throws java.lang.IllegalArgumentException if the specified index is invalid * @throws java.lang.IllegalArgumentException if the index has no key associated with */ public void changeKey(int i, Key key) { if (i < 0 || i >= nmax) throw new IllegalArgumentException(); if (! contains(i)) throw new NoSuchElementException("Specified index is not in the queue"); Key tmp = keys[i+d]; keys[i+d] = key; if (comp.compare(key, tmp) <= 0) { swim(qp[i+d]);} else { sink(qp[i+d]);} } /** * Decreases the key associated with index i to the given key * Worst case is O(log-d(n)) * @param i an index * @param key the key to associate with i * @throws java.lang.IllegalArgumentException if the specified index is invalid * @throws java.util.NoSuchElementException if the index has no key associated with * @throws java.lang.IllegalArgumentException if the given key is greater than the current key */ public void decreaseKey(int i, Key key) { if (i < 0 || i >=nmax) throw new IllegalArgumentException(); if (! contains(i)) throw new NoSuchElementException("Specified index is not in the queue"); if (comp.compare(keys[i+d], key) <= 0) throw new IllegalArgumentException("Calling with this argument would not decrease the Key"); keys[i+d] = key; swim(qp[i+d]); } /** * Increases the key associated with index i to the given key * Worst case is O(d*log-d(n)) * @param i an index * @param key the key to associate with i * @throws java.lang.IllegalArgumentException if the specified index is invalid * @throws java.util.NoSuchElementException if the index has no key associated with * @throws java.lang.IllegalArgumentException if the given key is lower than the current key */ public void increaseKey(int i, Key key) { if (i < 0 || i >=nmax) throw new IllegalArgumentException(); if (! contains(i)) throw new NoSuchElementException("Specified index is not in the queue"); if (comp.compare(keys[i+d], key) >= 0) throw new IllegalArgumentException("Calling with this argument would not increase the Key"); keys[i+d] = key; sink(qp[i+d]); } /** * Deletes the key associated to the given index * Worst case is O(d*log-d(n)) * @param i an index * @throws java.lang.IllegalArgumentException if the specified index is invalid * @throws java.util.NoSuchElementException if the given index has no key associated with */ public void delete(int i) { if (i < 0 || i >= nmax) throw new IllegalArgumentException(); if (! contains(i)) throw new NoSuchElementException("Specified index is not in the queue"); int idx = qp[i+d]; exch(idx, --n); swim(idx); sink(idx); keys[i+d] = null; qp[i+d] = -1; } /*************************** * General helper functions **************************/ //Compares two keys private boolean greater(int i, int j) { return comp.compare(keys[pq[i+d]+d], keys[pq[j+d]+d]) > 0; } //Exchanges two keys private void exch(int x, int y) { int i = x+d, j = y+d; int swap = pq[i]; pq[i] = pq[j]; pq[j] = swap; qp[pq[i]+d] = x; qp[pq[j]+d] = y; } /*************************** * Functions for moving upward or downward **************************/ //Moves upward private void swim(int i) { if (i > 0 && greater((i-1)/d, i)) { exch(i, (i-1)/d); swim((i-1)/d); } } //Moves downward private void sink(int i) { if (d*i+1 >= n) return; int min = minChild(i); while (min < n && greater(i, min)) { exch(i, min); i = min; min = minChild(i); } } /*************************** * Deletes the minimum child **************************/ //Return the minimum child of i private int minChild(int i) { int loBound = d*i+1, hiBound = d*i+d; int min = loBound; for (int cur = loBound; cur <= hiBound; cur++) { if (cur < n && greater(min, cur)) min = cur; } return min; } /*************************** * Iterator **************************/ /** * Gets an Iterator over the indexes in the priority queue in ascending order * The Iterator does not implement the remove() method * iterator() : Worst case is O(n) * next() : Worst case is O(d*log-d(n)) * hasNext() : Worst case is O(1) * @return an Iterator over the indexes in the priority queue in ascending order */ public Iterator iterator() { return new MyIterator(); } //Constructs an Iterator over the indices in linear time private class MyIterator implements Iterator { IndexMultiwayMinPQ clone; public MyIterator() { clone = new IndexMultiwayMinPQ(nmax, comp, d); for (int i = 0; i < n; i++) { clone.insert(pq[i+d], keys[pq[i+d]+d]); } } public boolean hasNext() { return !clone.isEmpty(); } public Integer next() { if (!hasNext()) throw new NoSuchElementException(); return clone.delMin(); } public void remove() { throw new UnsupportedOperationException(); } } /*************************** * Comparator **************************/ //default Comparator private class MyComparator implements Comparator { @Override public int compare(Key key1, Key key2) { return ((Comparable) key1).compareTo(key2); } } } /****************************************************************************** * Copyright 2002-2022, Robert Sedgewick and Kevin Wayne. * * This file is part of algs4.jar, which accompanies the textbook * * Algorithms, 4th edition by Robert Sedgewick and Kevin Wayne, * Addison-Wesley Professional, 2011, ISBN 0-321-57351-X. * http://algs4.cs.princeton.edu * * * algs4.jar is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * algs4.jar is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with algs4.jar. If not, see http://www.gnu.org/licenses. ******************************************************************************/